Deep Fried Offshore |
Stephen worked for an Initech that sold specialized hardware: high-performance, high-throughput systems for complex data processing tasks in the enterprise world, sold at exorbitant enterprise prices. Once deployed, these systems were configured via a management app that exposed an HTTP interface, just like any consumer-grade router or Wi-Fi access point that is configurable through a website (e.g. 192.168.0.1).
Stephen worked with a diverse team of American engineers who were finishing up the management application for a new model. The product was basically done but needed a little bit of testing and polish before the official release. They expected several months of post-release work and then the project would go into maintenance mode.
Then disaster struck. A pointy-haired boss somewhere up in a fuzzy area of the organization chart simply labeled Here be VPs discovered the large salary difference between American engineers and off-shore workers, and decided American engineers were far too expensive for software maintenance. The company decided to lay off 300 software engineers and hire 300 fresh-out-of-college replacements in a foreign country with much lower labor rates. The announcement was overshadowed by the fanfare of the products release and proudly billed as a major win for the company.
Stephen was lucky enough to stay on and shift to other projects, the first of which was to assist with the transition by documenting everything he could for the new team. Which he did, in hundreds of pages of explicit detail, explaining how to use the source control repository for the project, execute and log unit tests, and who to contact when they had questions regarding the hardware itself. After that, he devoted himself to other projects. Months turned into years and Stephen assumed by the silence that the handover was successful and hed never see the management app again.
Of course that isnt what happened.
Theres an old joke on the Internet called How to shoot yourself in the foot (http://www.toodarkpark.org/computers/humor/shoot-self-in-foot.html) that lampoons the complicated process of shooting yourself in the foot in various programming languages. Here is one such entry:
370 JCL: You send your foot down to MIS and include a 300-page document explaining exactly how you want it to be shot. Three years later, your foot returns deep-fried.
Three years after the management app was passed off to the new offshore engineer team, a new panic was boiling through Initech. The management application was as slow as molasses, buggier than flies in soup, with a user interface that was battered and deep-fried in a nonsense language that vaguely resembled English. It also crashed a lot, each time requiring a power reset to bring the system back up.
Initech had simmered along by only shipping the original version, but now they had this expensive, powerful product with three years worth of hardware and firmware updates that the management app could not configure and thus were not available to customers. Several important customers canceled their support contracts rather than pay for half-baked updates that rarely worked, and many prospective customers passed them right by when basic features printed in the product brochure were unavailable for demo. They were falling behind in the industry.
It was bad enough that management finally decided to do something.
That something was to toss the offshore team and see how many of their old laid-off engineers they could scoop up, which was, not surprisingly, few. As one of the few original engineers who still worked there, Stephen was whisked away from his current projects to assist.
With a bite-sized portion of the old team re-assembled, they set to work to unscramble the situation. Stephen noticed that the last source control check-in was from three years ago. Upon further inspection, he realized that not one of the offshore engineers had ever committed.
He called up the offshore office to see if they had their own repository. The last few remaining offshore workers didnt know, and when told to search every system they had, they replied they couldnt because their local manager had sold all of their computer equipment on an auction site upon learning of their impending layoffs.
He then contacted the hardware team in hopes that maybe somebody had a clone of the offshore teams repository, but the hardware guys claimed the software team was laid off three years ago and they were not aware of an offshore replacement team.
With no current source code, and an old repository that hadnt been committed to in three years, Stephen and the team were left with no choice but to pick up exactly where they left off three years ago&
[Advertisement] Manage IT infrastructure as code across all environments with Puppet. Puppet Enterprise now offers more control and insight, with role-based access control, activity logging and all-new Puppet Apps. Start your free trial today!
|
Метки: Feature Articles |
CodeSOD: data-wheel="reinvented" |
In HTML5, the data-* attributes were codified, and this is a really nice thing for building applications. They are an app-defined namespace to attach any sorts of custom data to your HTML attributes. For example, a div responsible for displaying a User object might have an attribute like
div[data-user-id].
Im not the only one who thinks theyre a nice feature. Eric W has a co-worker whos come up with a very… unique way of using them. First, he has the following Django template:
{% for cat in categories %}
{{ cat.name }}
{% endfor %}
Which generates links like: Housewares
Obviously, since the href points nowhere, there must be a JavaScript event handler. I wonder what it does…
$('.shop-nav a').unbind('click').click(function(e){
e.stopPropagation();
var page = $(this).data('type');
window.location.href = window.location.origin+'/shop/'+ page;
return false;
})
This is one of my favorite classes of bad code- the one where a developer uses a new feature to completely reinvent an old feature, badly. This JavaScript uses the data-type attribute to hold what should actually be in the href. Instead of having real navigation, we hijack the click event to force navigation. The entire JavaScript could be replaced in favor of a link like this: Housewares.
Maybe they need data-type because theres a CSS rule that styles it?
[Advertisement] Manage IT infrastructure as code across all environments with Puppet. Puppet Enterprise now offers more control and insight, with role-based access control, activity logging and all-new Puppet Apps. Start your free trial today!
|
Метки: CodeSOD |
Test Overflow |

WidCo was a victim of its own success. It had started as a small purveyor of widgets: assembling, storing, transporting, and shipping the widgets to their small client base in their podunk state. They'd once had the staff to fill orders placed by phone. As they'd begun to make a name for themselves in the surrounding tri-state region, however, their CEO had caught wise to the value of "this Internet thing."
Within a decade, they were not only the country's foremost producer of widgets, but their entire staff makeup had changed. They now had more employees in IT than the other departments combined, and relied on in-house software to manage inventory, take orders, and fulfill them in a timely fashion. If the software went down, they could no longer fall back on a manual process.
And—as the IT manager was fond of pointing out in budget meetings—they had a QA department of 0 and no automated tests.
Bug reports piled up. Who had time to fix bugs? Monday was for scrambling to remedy production incidents after the weekend's jobs ran, Tuesday was for slapping together something sensible out of requirements documents, Wednesday for coding, Thursday for manual testing, and Friday for shoving half-tested code into production. Agile!
Finally, over the post-Christmas slump, the IT manager managed to convince the CTO to bring in a trainer to teach developers about "improving our unit tests". All 0 of them.
Adding tests to existing code was deemed a waste of time. It compiles, therefore it works. Begrudgingly, the CTO admitted that unit tests might be a good idea for new applications. Sometime in the next decade, they were bound to build a new application. They'd do this testing thing then.
Desperate, the IT manager put in place a code review policy: before anything could be deployed, someone had to look at it. But they were checking only the changes, the CTO pointed out. It was a waste of time to examine working code in production. Standards were seen as just documentation, and documentation was waste. Look at the existing code and do more of that.
"Think lean," the CTO said.
The IT manager sighed and hung his head.
And then a lucrative new contract was signed: WidCo would be selling widgets in Canada as well. When their distribution partner expressed concern about the lack of a QA department, the CTO loudly proclaimed that all their developers had QA training, and they were bringing in an engineer to streamline the testing process. A position promptly opened under the IT manager, who was seen with an actual, honest-to-God smile on his face for the first time all year.
Interview after interview was conducted. The first engineer was a bright young chap, a brunet with an easy smile and big round glasses. He started on a Tuesday, bringing in donuts to share with the team and promising to have things cleaned up within the week.
Two days later, he handed in his resignation, crawled into a bottle, and refused to answer his phone, muttering about raw pointers and RAII. Later, the team found him waiting tables at the local pub, his bright smile turned into a sullen sneer.
The second QA engineer was made of sterner stuff. She had the benefit of already being familiar with the code: she'd been brought in as a development contractor to save a project that was running over deadline earlier that year, and while she hadn't managed to work a miracle, she did impress the IT manager.
By now, the CTO was so over the whole QA engineer thing. He was onto this newfangled "DTAP" standard, declaring that he'd stand up a Development server, a Test server, an Acceptance server, and a Production server, and all code would be promoted between them instead of going right from the developers' machines to prod.
And so the QA Engineer rolled up her sleeves and tried to develop a sane promotions process. She set up an instance of Subversion and stuffed all the code into it. In order to comply with the standard, she made four branches: Dev, QA, Acceptance, and Trunk. Code would be done in dev, merged into QA for testing, merged into Acceptance for acceptance testing, and merged into Production to deploy. A daily cron job would push the code onto each server automatically. Continuous Integration!
The build system complete, she could get to her main job: testing code on the Testing server. She sat the project managers in a room and explained how to test on the Acceptance server. Within a month, however, she was doing it for them instead.
At least we're testing, she told herself, resigned to switching hats between "Try to break it" mode and "Is it actually nice to use?" mode when she changed servers.
Of course, there were 30 developers and only 1 QA engineer, so she didn't have time to go through changes one by one. Instead, she'd test a batch of them on a weekly schedule. But by the time they were merged together and pushed to the servers, she had no idea which change was responsible for a test failing. So all the tickets in that batch would have to be failed, and the whole thing reverted back to the dev branch. Sometimes multiple times per batch.
It was getting better, though. Every batch had fewer issues. Every code review had fewer comments. The QA engineer was beginning to see the light at the end of the tunnel. Maybe this would get to be so low-key she could handle it. Maybe she'd even be rewarded for her herculean efforts. Maybe she'd get a second helper. Maybe things would be okay.
Then the CTO read an article about how some big-name companies had "staging servers," and declared that they ought to have one to improve quality. After code was accepted, code would then be deployed to the staging server for a round of regression testing before it could go live. Of course, since they had a dedicated tester, that'd simply be her responsibility.
On the way out of that meeting, her heart sinking, the QA engineer was stopped in the hall by a project manager. "We've noticed a lot of tickets are failing," he began with a stern look. "That looks bad on our reports, so we're going to be running an extra round of testing on the dev server before it gets to you."
"Oh, good, so maybe there'll be less tickets I have to send back," she said, raking a hand through her hair.
"Exactly," he said with a hint of a smile. "I'll forward the invite to you so you can sit in and give your feedback. It'll be 4 hours on Monday afternoon."
Her efforts to weasel out of it were to no avail. She was the QA expert, after all, and her job was to train anyone who needed training. Henceforth, code would be checked into dev, tested by the project management group and the QA Engineer, moved to QA, checked by the QA Engineer, moved to Acceptance, checked by the QA Engineer, moved to Staging, checked by the QA Engineer, and then finally moved into production.
After a few failure cycles in QA (somehow, the project managers weren't very effective at finding bugs), she was staying late into the evening on Friday nights, testing code as fast as possible so it could go to production before it was technically Saturday and thus overdue.
Of course, because only she knew when the code was ready to move, the QA Engineer found herself in charge of merging between branches. And she had no idea what the code even did anymore. She never got to work with it, only seeing things from the UI level. And she was usually exhausted when she had to merge things. So there began to be a new class of error: merge errors, introduced by sloppy testers.
The CTO had a brillant solution to that as well. He split up the shared libraries, making multiple copies of the repository. Each team would have their own stack of DTASP servers and their own version of source code. That way, they could deploy without having to merge code belonging to different teams. In addition to their existing separate dev instances, all 10 3-man teams would get 8 servers each: test, acceptance, staging, and prod, plus a database for each. And the QA engineer would have to test all of them.
These efforts failed to make a dent in WidCo's bug backlog. However, the QA engineer made a killing running a betting pool in the breakroom. The odds were calculated fresh every Monday morning, and the payout went out when metrics were pulled on Friday afternoon. There was only one question to gamble on.
Which was larger this week: the average time required to deploy a fix, or the average time for a developer to leave for a new job?
[Advertisement]
Incrementally adopt DevOps best practices with BuildMaster, ProGet and Otter, creating a robust, secure, scalable, and reliable DevOps toolchain.
|
Метки: Feature Articles |
CodeSOD: Keeping it Regular |
Regular expressions are like one of those multi-tools: they're a knife, they're a screwdriver, they're pliers, and there's a pair of tweezers stuck in the handle. We can use them to do anything.
For example, Linda inherited a site that counted up and down votes, like Reddit, implemented in CoffeeScript. Instead of using variables or extracting the text from the DOM, this code brought regular expressions to bear.
updateBookmarkCounter = (upOrDown) ->
counterSpan = $('.bookmark .counter')
spanHtml = counterSpan.html()
count = spanHtml.match(/\d/).first().toInteger()
newCount = if (upOrDown == 'down') then (count - 1) else (count + 1)
newCount = 0 if newCount < 1
counterSpan.html(spanHtml.replace(/\d/, newCount))
updateUserBookmarkCount upOrDown
Theres a glitch in this code, and no, its not that this code exists in the first place. Think about what happens when the number of votes exceeds 10.
Okay, maybe not the best use of regexes. What about sanitizing inputs? That seems like a textbook use case. Alexander Ts co-worker found a very clever way to convert any input into a floating point number. Any input.
function convertToFloat(value) {
if(typeof value == "number") return value;
return parseFloat(value.replace(/\D/g, ''));
}
Values like D12.3 convert seamlessly to 123.
You know what else regexes can do? Parse things! Not just HTML and XML, but anything. Like, for example, parsing out an INI file. Kate found this.
$ini = array();
preg_match_all('/\[(?[^\]\r\n]+)\][\s\r\n]*(?([^\r\n\[=]+(=[^\r\n]+)?[\s\r\n]*)+)?/i', file_get_contents($iniFile), $ini);
foreach($ini['sections'] as $i=>$section) {
$ini[$section] = array();
$values = $sections['values'][$i];
preg_match_all('/[\s]*(?[^\r\n\=]+)(=(?[^\r\n]+))?[\r\n]*/i',$values,$ini);
foreach($ini['names'] as $j=>$name) {
$name = trim($name);
if($name && !preg_match("/[#;]/", $name)){
$value = trim($ini['values'][$j]);
if(!preg_match("/[#;]/", $value))
$ini[$section][$name] = $value;
}
}
}
She was able to replace that entire block with $ini = parse_ini_file($iniFile, true);. parse_ini_file is a built-in library function in PHP.
Speaking of parsing, R.J. L. works for a company that runs printed documents through optical character recognition, and then uses regular expressions to identify interesting parts of the document to store in the database. These regular expressions are written in a custom, in-house regex language, that is almost but not quite a PCRE. By using their own regular expression language, tasks that might be inelegant or complicated in traditional languages become simple. For example, this regex find the document identifier on any page.
([:-.,;/\\(]{0,2}(( [C|c][P|p][K,<|k,<][0-9]{11}
)||([:#.$",'#-/|][C|c][P|p][K,<|k,<][0-9]{11} )||( [C|c][P|p][K,<|k,<][0-9]{11}[
:.$",'#-/|l\\])||([:.$",'#-/|][C|c][P|p][K,<|k,<][0-9]{11}[:.$",'#-/|l\\])||(
01[A|a|C|c|D|d|E|e|R|r][0-9]{7} )||([:#.$",'#-/|]01[A|a|C|c|D|d|E|e|R|r][0-9]{7}
)||(01[A|a|C|c|D|d|E|e|R|r][0-9]{7}[:#.$",'#-/|l\\])||([:#.$",'#-/|]01[A|a|C|c|D
|d|E|e|R|r][0-9]{7}[:#.$",'#-/|l\\])||( 02[A|a|B|b|C|c|D|d|E|e|F|f][0-9]{7}
)||([:#.$",'#-/|]02[A|a|B|b|C|c|D|d|E|e|F|f][0-9]{7} )||( 02[A|a|B|b|C|c|D|d|E|e
|F|f][0-9]{7}[:#.$",'#-/|l\\])||([:#-/|]02[A|a|B|b|C|c|D|d|E|e|F|f][0-9]{7}[:#.$
",'#-/|l\\])||( 04[C|c|D|d|F|f|V|v][0-9]{7}
)||([:#.$",'#-/|]04[C|c|D|d|F|f|V|v][0-9]{7} )||( 04[C|c|D|d|F|f|V|v][0-9]{7}[:#
.$",'#-/|l\\])||([:#.$",'#-/|]04[C|c|D|d|F|f|V|v][0-9]{7}[:#.$",'#-/|l\\])||(
05[M|m|A|a][0-9]{7} )||([:#.$",'#-/|]05[M|m|A|a][0-9]{7} )||( 05[M|m|A|a][0-9]{7
}[:#.$",'#-/|l\\])||([:#.$",'#-/|]05[M|m|A|a][0-9]{7}[:#.$",'#-/|l\\])||(
06[B|b|C|c|G|g|H|h|J|j|K|k|L|l|M|m|S|s|U|u|Y|y][0-9]{7}
)||([:#.$",'#-/|]06[B|b|C|c|G|g|H|h|J|j|K|k|L|l|M|m|S|s|U|u|Y|y][0-9]{7} )||( 06
[B|b|C|c|G|g|H|h|J|j|K|k|L|l|M|m|S|s|U|u|Y|y][0-9]{7}[:#.$",'#-/|l\\])||([:#.$",
'#-/|]06[B|b|C|c|G|g|H|h|J|j|K|k|L|l|M|m|S|s|U|u|Y|y][0-9]{7}[:#.$",'#-/|l\\])||
( 07[U|u][0-9]{7} )||([:#.$",'#-/|]07[U|u][0-9]{7} )||( 07[U|u][0-9]{7}[:#.$",'#
-/|l\\])||([:#.$",'#-/|]07[U|u][0-9]{7}[:#.$",'#-/|l\\])||( 08[A|a][0-9]{7}
)||([:#.$",'#-/|]08[A|a][0-9]{7} )||( 08[A|a][0-9]{7}[:#.$",'#-/|l\\])||([:#.$",
'#-/|]08[A|a][0-9]{7}[:#.$",'#-/|l\\])||( 09[A|a|B|b|C|c|D|d|F|f][0-9]{7}
)||([:#.$",'#-/|]09[A|a|B|b|C|c|D|d|F|f][0-9]{7} )||( 09[A|a|B|b|C|c|D|d|F|f][0-
9]{7}[:#.$",'#-/|l\\])||([:#.$",'#-/|]09[A|a|B|b|C|c|D|d|F|f][0-9]{7}[:#.$",'#-/
|l\\])||( 10[M|m|F|f][0-9]{7} )||([:#.$",'#-/|]10[M|m|F|f][0-9]{7} )||( 10[M|m|F
|f][0-9]{7}[:#.$",'#-/|l\\])||([:#.$",'#-/|]10[M|m|F|f][0-9]{7}[:#.$",'#-/|l\\])
||( 13[A|a][0-9]{7} )||([:#.$",'#-/|]13[A|a][0-9]{7} )||( 13[A|a][0-9]{7}[:#.$",
'#-/|l\\])||([:#.$",'#-/|]13[A|a][0-9]{7}[:#.$",'#-/|l\\])||( 14[A|a][0-9]{7}
)||([:#.$",'#-/|]14[A|a][0-9]{7} )||(
14[A|a][0-9]{7}[:#.$",'#-/|l\\])||([:#.$",'#-/|]14[A|a][0-9]{7})||(
15[D|d|E|e|R|r|T|t][0-9]{7} )||([:#.$",'#-/|]15[D|d|E|e|R|r|T|t][0-9]{7} )||( 15
[D|d|E|e|R|r|T|t][0-9]{7}[:#.$",'#-/|l\\])||([:#.$",'#-/|]15[D|d|E|e|R|r|T|t][0-
9]{7}[:#.$",'#-/|l\\])||( 17[A|a|E|e|L|l|M|m|P|p|S|s|U|u|W|w][0-9]{7}
)||([:#.$",'#-/|]17[A|a|E|e|L|l|M|m|P|p|S|s|U|u|W|w][0-9]{7} )||( 17[A|a|E|e|L|l
|M|m|P|p|S|s|U|u|W|w][0-9]{7}[:#.$",'#-/|l\\])||([:#.$",'#-/|]17[A|a|E|e|L|l|M|m
|P|p|S|s|U|u|W|w][0-9]{7}[:#.$",'#-/|l\\])||( 18[A|a][0-9]{7}
)||([:#.$",'#-/|]18[A|a][0-9]{7} )||( 18[A|a][0-9]{7}[:#.$",'#-/|l\\])||([:#.$",
'#-/|]18[A|a][0-9]{7}[:#.$",'#-/|l\\])||( 21[A|a|C|c|D|d][0-9]{7}
)||([:#.$",'#-/|]21[A|a|C|c|D|d][0-9]{7} )||( 21[A|a|C|c|D|d][0-9]{7}[:#.$",'#-/
|l\\])||([:#.$",'#-/|]21[A|a|C|c|D|d][0-9]{7}[:#.$",'#-/|l\\])||(
23[A|a|B|b|C|c|D|d|L|l|M|m][0-9]{7}
)||([:#.$",'#-/|]23[A|a|B|b|C|c|D|d|L|l|M|m][0-9]{7} )||(23[A|a|B|b|C|c|D|d|L|l|
M|m][0-9]{7}[:#.$",'#-/|l\\])||([:#.$",'#-/|]23[A|a|B|b|C|c|D|d|L|l|M|m][0-9]{7}
[:#.$",'#-/|l\\]) ||( 24[A|a|B|b|C|c|F|f|K|k|M|m|T|t][0-9]{7}
)||([:#.$",'#-/|]24[A|a|B|b|C|c|F|f|K|k|M|m|T|t][0-9]{7} )||( 24[A|a|B|b|C|c|F|f
|K|k|M|m|T|t][0-9]{7}[:#.$",'#-/|l\\])||([:#.$",'#-/|]24[A|a|B|b|C|c|F|f|K|k|M|m
|T|t][0-9]{7}[:#.$",'#-/|l\\]) ||( 25[A|a][0-9]{7}
)||([:#.$",'#-/|]25[A|a][0-9]{7} )||( 25[A|a][0-9]{7}[:#.$",'#-/|l\\])||([:#.$",
'#-/|]25[A|a][0-9]{7}[:#.$",'#-/|l\\])||( 32[A|a|F|f|H|h|X|x|Y|y|Z|z][0-9]{7}
)||([:#.$",'#-/|]32[A|a|F|f|H|h|X|x|Y|y|Z|z][0-9]{7} )||( 32[A|a|F|f|H|h|X|x|Y|y
|Z|z][0-9]{7}[:#.$",'#-/|l\\])||([:#.$",'#-/|]32[A|a|F|f|H|h|X|x|Y|y|Z|z][0-9]{7
}[:#.$",'#-/|l\\])||( 34[A|a][0-9]{7} )||([:#.$",'#-/|]34[A|a][0-9]{7} )||(
34[A|a][0-9]{7}[:#.$",'#-/|l\\])||([:#.$",'#-/|]34[A|a][0-9]{7}[:#.$",'#-/|l\\])
||( 35[A|a|B|R|r|S|s|T|t|U|u][0-9]{7}
)||([:#.$",'#-/|]35[A|a|B|R|r|S|s|T|t|U|u][0-9]{7} )||( 35[A|a|B|R|r|S|s|T|t|U|u
][0-9]{7}[:#.$",'#-/|l\\])||([:#.$",'#-/|]35[A|a|B|R|r|S|s|T|t|U|u][0-9]{7}[:#.$
",'#-/|l\\])||( 39[C|c|P|p][0-9]{7} )||([:#.$",'#-/|]39[C|c|P|p][0-9]{7} )||( 39
[C|c|P|p][0-9]{7}[:#.$",'#-/|l\\])||([:#.$",'#-/|]39[C|c|P|p][0-9]{7}[:#.$",'#-/
|l\\])||( 40[A|a|C|c|D|d|S|s][0-9]{7}
)||([:#.$",'#-/|]40[A|a|C|c|D|d|S|s][0-9]{7} )||( 40[A|a|C|c|D|d|S|s][0-9]{7}[:#
.$",'#-/|l\\])||([:#.$",'#-/|]40[A|a|C|c|D|d|S|s][0-9]{7}[:#.$",'#-/|l\\])||(
46[A|a|B|b][0-9]{7} )||([:#.$",'#-/|]46[A|a|B|b][0-9]{7} )||( 46[A|a|B|b][0-9]{7
}[:#.$",'#-/|l\\])||([:#.$",'#-/|]46[A|a|B|b][0-9]{7}[:#.$",'#-/|l\\]) ||(
01[A|a|C|c|D|d|E|e|R|r][0-9]{9} )||([:#.$",'#-/|]01[A|a|C|c|D|d|E|e|R|r][0-9]{9}
)||(01[A|a|C|c|D|d|E|e|R|r][0-9]{9}[:#.$",'#-/|l\\])||([:#.$",'#-/|]01[A|a|C|c|D
|d|E|e|R|r][0-9]{9}[:#.$",'#-/|l\\])||( 02[A|a|B|b|C|c|D|d|E|e|F|f][0-9]{9} )
||([:#.$",'#-/|]02[A|a|B|b|C|c|D|d|E|e|F|f][0-9]{9} )||( 02[A|a|B|b|C|c|D|d|E|e|
F|f][0-9]{9}[:#.$",'#-/|l\\])||([:#.$",'#-/|]02[A|a|B|b|C|c|D|d|E|e|F|f][0-9]{9}
[:#.$",'#-/|l\\]) ||( 04[C|c|D|d|F|f|V|v][0-9]{9}
)||([:#.$",'#-/|]04[C|c|D|d|F|f|V|v][0-9]{9} )||( 04[C|c|D|d|F|f|V|v][0-9]{9}[:#
.$",'#-/|l\\])||([:#.$",'#-/|]04[C|c|D|d|F|f|V|v][0-9]{9}[:#.$",'#-/|l\\])||(
05[M|m|A|a][0-9]{9} )||([:#.$",'#-/|]05[M|m|A|a][0-9]{9} )||( 05[M|m|A|a][0-9]{9
}[:#.$",'#-/|l\\])||([:#.$",'#-/|]05[M|m|A|a][0-9]{9}[:#.$",'#-/|l\\])||(
06[B|b|C|c|G|g|H|h|J|j|K|k|L|l|M|m|S|s|U|u|Y|y][0-9]{9}
)||([:#.$",'#-/|]06[B|b|C|c|G|g|H|h|J|j|K|k|L|l|M|m|S|s|U|u|Y|y][0-9]{9} )||( 06
[B|b|C|c|G|g|H|h|J|j|K|k|L|l|M|m|S|s|U|u|Y|y][0-9]{9}[:#.$",'#-/|l\\])||([:#.$",
'#-/|]06[B|b|C|c|G|g|H|h|J|j|K|k|L|l|M|m|S|s|U|u|Y|y][0-9]{9}[:#.$",'#-/|l\\])||
( 07[U|u][0-9]{9} )||([:#.$",'#-/|]07[U|u][0-9]{9} )||( 07[U|u][0-9]{9}[:#.$",'#
-/|l\\])||([:#.$",'#-/|]07[U|u][0-9]{9}[:#.$",'#-/|l\\])||( 08[A|a][0-9]{9}
)||([:#.$",'#-/|]08[A|a][0-9]{9} )||( 08[A|a][0-9]{9}[:#.$",'#-/|l\\])||([:#.$",
'#-/|]08[A|a][0-9]{9}[:#.$",'#-/|l\\])||( 09[A|a|B|b|C|c|D|d|F|f][0-9]{9} )||
([:#.$",'#-/|]09[A|a|B|b|C|c|D|d|F|f][0-9]{9} )||( 09[A|a|B|b|C|c|D|d|F|f][0-9]{
9}[:#.$",'#-/|l\\])||([:#.$",'#-/|]09[A|a|B|b|C|c|D|d|F|f][0-9]{9}[:#.$",'#-/|l\
\])||( 10[M|m|F|f][0-9]{9} )||([:#.$",'#-/|]10[M|m|F|f][0-9]{9} )||( 10[M|m|F|f]
[0-9]{9}[:#.$",'#-/|l\\])||([:#.$",'#-/|]10[M|m|F|f][0-9]{9}[:#.$",'#-/|l\\])||(
13[A|a][0-9]{9} )||([:#.$",'#-/|]13[A|a][0-9]{9} )||( 13[A|a][0-9]{9}[:#.$",'#-/
|l\\])||([:#.$",'#-/|]13[A|a][0-9]{9}[:#.$",'#-/|l\\])||( 14[A|a][0-9]{9} )||
([:#.$",'#-/|]14[A|a][0-9]{9} )||( 14[A|a][0-9]{9}[:#.$",'#-/|l\\])||([:#.$",'#-
/|]14[A|a][0-9]{9}[:#.$",'#-/|l\\])|| ( 15[D|d|E|e|R|r|T|t][0-9]{9}
)||([:#.$",'#-/|]15[D|d|E|e|R|r|T|t][0-9]{9} )||( 15[D|d|E|e|R|r|T|t][0-9]{9}[:#
.$",'#-/|l\\])||([:#.$",'#-/|]15[D|d|E|e|R|r|T|t][0-9]{9}[:#.$",'#-/|l\\])||(
17[A|a|E|e|L|l|M|m|P|p|S|s|U|u|W|w][0-9]{9}
)||([:#.$",'#-/|]17[A|a|E|e|L|l|M|m|P|p|S|s|U|u|W|w][0-9]{9} )||( 17[A|a|E|e|L|l
|M|m|P|p|S|s|U|u|W|w][0-9]{9}[:#.$",'#-/|l\\])||([:#.$",'#-/|]17[A|a|E|e|L|l|M|m
|P|p|S|s|U|u|W|w][0-9]{9}[:#.$",'#-/|l\\])||( 18[A|a][0-9]{9}
)||([:#.$",'#-/|]18[A|a][0-9]{9} )||(
18[A|a][0-9]{9}[:#.$",'#-/|l\\])||([:#.$",'#-/|]18[A|a][0-9]{9}[:#.$",'#-/|l\\])
||( 21[A|a|C|c|D|d][0-9]{9} )||([:#.$",'#-/|]21[A|a|C|c|D|d][0-9]{9} )||( 21[A|a
|C|c|D|d][0-9]{9}[:#.$",'#-/|l\\])||([:#.$",'#-/|]21[A|a|C|c|D|d][0-9]{9}[:#.$",
'#-/|l\\])||( 23[A|a|B|b|C|c|D|d|L|l|M|m][0-9]{9}
)||([:#.$",'#-/|]23[A|a|B|b|C|c|D|d|L|l|M|m][0-9]{9} )||( 23[A|a|B|b|C|c|D|d|L|l
|M|m][0-9]{9}[:#.$",'#-/|l\\])||([:#.$",'#-/|]23[A|a|B|b|C|c|D|d|L|l|M|m][0-9]{9
}[:#.$",'#-/|l\\])||( 24[A|a|B|b|C|c|F|f|K|k|M|m|T|t][0-9]{9}
)||([:#.$",'#-/|]24[A|a|B|b|C|c|F|f|K|k|M|m|T|t][0-9]{9} )||( 24[A|a|B|b|C|c|F|f
|K|k|M|m|T|t][0-9]{9}[:#.$",'#-/|l\\])||([:#.$",'#-/|]24[A|a|B|b|C|c|F|f|K|k|M|m
|T|t][0-9]{9}[:#.$",'#-/|l\\])||( 25[A|a][0-9]{9}
)||([:#.$",'#-/|]25[A|a][0-9]{9} )||(
25[A|a][0-9]{9}[:#.$",'#-/|l\\])||([:#.$",'#-/|]25[A|a][0-9]{9}[:#.$",'#-/|l\\])
||( 32[A|a|F|f|H|h|X|x|Y|y|Z|z][0-9]{9}
)||([:#.$",'#-/|]32[A|a|F|f|H|h|X|x|Y|y|Z|z][0-9]{9} )||( 32[A|a|F|f|H|h|X|x|Y|y
|Z|z][0-9]{9}[:#.$",'#-/|l\\])||([:#.$",'#-/|]32[A|a|F|f|H|h|X|x|Y|y|Z|z][0-9]{9
}[:#.$",'#-/|l\\])||( 34[A|a][0-9]{9} )||([:#.$",'#-/|]34[A|a][0-9]{9} )||( 34[A
|a][0-9]{9}[:#.$",'#-/|l\\])||([:#.$",'#-/|]34[A|a][0-9]{9}[:#.$",'#-/|l\\])||(
35[A|a|B|b|R|r|S|s|T|t|U|u][0-9]{9}
)||([:#.$",'#-/|]35[A|a|B|b|R|r|S|s|T|t|U|u][0-9]{9} )||( 35[A|a|B|b|R|r|S|s|T|t
|U|u][0-9]{9}[:#.$",'#-/|l\\])||([:#.$",'#-/|]35[A|a|B|b|R|r|S|s|T|t|U|u][0-9]{9
}[:#.$",'#-/|l\\])||( 39[C|c|P|p][0-9]{9} )||([:#.$",'#-/|]39[C|c|P|p][0-9]{9}
)||( 39[C|c|P|p][0-9]{9}[:#.$",'#-/|l\\])||([:#.$",'#-/|]39[C|c|P|p][0-9]{9}[:#.
$",'#-/|l\\]) ||( 40[A|a|C|c|D|d|S|s][0-9]{9}
)||([:#.$",'#-/|]40[A|a|C|c|D|d|S|s][0-9]{9} )||( 40[A|a|C|c|D|d|S|s][0-9]{9}[:#
.$",'#-/|l\\])||([:#.$",'#-/|]40[A|a|C|c|D|d|S|s][0-9]{9}[:#.$",'#-/|l\\])||(
46[A|a|B|b][0-9]{9} )||([:#.$",'#-/|]46[A|a|B|b][0-9]{9} )||( 46[A|a|B|b][0-9]{9
}[:#.$",'#-/|l\\])||([:#.$",'#-/|]46[A|a|B|b][0-9]{9}[:#.$",'#-/|l\\]))[-.,;:Il|
/\\]{0,2} )
Simplicity itself.
|
Метки: CodeSOD |
Error'd: The Duke of Error'd |
"Ok, so what happens if I'm a duke? Then what?" wrote Adam S.
"I got this mysterious prompt on the long-haul flight from Auckland to London," wrote Ben, "I'm sad to say I chickened out and chose 'No Thanks'"
"Incorporated in May 2015, and it's now 2016, been in business for 46 years, so... they must be selling time machines!" wrote James.
Frank G. writes, "To be fair, he did tell us that outsiders are trying to sneak in and attack US citizens."
"Bruxelles-Midi introduced their new 41:10 info screens, just the resolution of 320x78 could be a tad higher," Alexander writes.
"Given that I'm not supposed to see this, should I close my eyes?" writes Craig W.
"So, if unboxing SQL Server 2016 completed successfully, where's the box?!" wrote Maxwell.
[Advertisement] Release!
is a light card game about software and the people who make it. Play with 2-5 people, or up to 10 with two copies - only $9.95 shipped!
|
Метки: Error'd |
The Contractor |
As developers, we often find ourselves working in stupid ways because the folks who were hired above/before us think that what they set up is ideal. While this happens in numerous industries, finance, especially at huge conglomerates, takes IT/Software-WTF to a whole new level. As contractors, we often get the we need your help in an emergency even though everything is unicorns and rainbows speech that precedes some meltdown for which they want you to take the blame.
After taking a contract position at a large financial company, Bryan T. expected to see some amazing things. On the interview, they talked a big game and had even bigger budgets. It didn't take long to see some amazing things; but not the kind of amazing you'd think.
To begin with, the managers and developers were still trying to roll their own time zones and caching. They didn't understand any of these terms: object graph, business intelligence services, concurrency, message pump, domain model, and well-defined. Bryan even needed to explain to them why JavaScript on random web pages doesn't have natural mechanisms to attach to .NET event handlers in other applications.
Their head DBA explained that the difference between a uniqueness constraint and a primary key was semantics, and that audit records and current records should always be stored in the same table so as to keep related data together. They even used a simple text column to store City, State and Country, which led to obvious issues like three different values for the US ("US", "USA", "US_TOTAL").
We all need to conform to some semblance of coding practices. These folks decided to use anti-coding-practices. For example, IEntity was a class but the "I" prefix was used because it was returned from an API.
Shared common libraries were not allowed; if you needed to re-use a chunk of code, copy and paste it to where you need it; that's why they implemented cut-n-paste across applications!
This also explains why SLOC is their primary productivity metric.
There were no planned releases or scheduled iterations; whenever someone barked, a snapshot was manually copied from local builds.
Perhaps most interesting of all, they had an awesome approach to branching. Instead of actually branching, they copied the whole code base into a new repository and ran that forward. Of course, this left a trail of repository droppings that you had to navigate.
It took Bryan quite a while to acclimate to all of this. Then the team received a massive product request. Unfortunately, nobody understood the concept of scalability, let alone why it had to be considered. Instead, they decided that Cowboy Coding would be the M.O. of the project.
At this point, Bryan decided he didn't want the job all that much. That very day, he had to work with another developer with whom he'd not yet had the pleasure. Their task was to return some JSON from a web service call. After more than a month of work, the other developer proudly showed Bryan what he had come up with to return specific data from the web service:
[
["Morning Workflow Status",
"http://initrodestatus:1234/dashboards/WTFProject/Morning%20Workflow%20Status"],
["Scrape Status",
"http://initrodestatus:1234/dashboards/WTFProject/Scrape%20Status"],
["UK Nominations Storage",
"http://initrodestorage:1234/dashboards/WTFProject/UK%20Nominations%20Storage"]
]
For Bryan, it was the last straw.
|
Метки: Feature Articles |
CodeSOD: Excellent Test |
These days, you arent just doing development. Your development has to be driven. Business driven. Domain driven. Test driven.
TDD is generally held up as a tool for improving developer efficiency and code quality. As it turns out, scholarly research doesnt support that: theres no indication that TDD has any impact at all.
I have to wonder, though, if maybe thats because people are still writing tests like this one, which Tomas received from an offshore contractor.
public void CompareValues(string con, int pf, int pt, string tableVal, int len, string Ledpos1)
{
string databasevalue1 = (con.Substring(pf-1,len)).Trim();
string tableval = tableVal.Trim();
switch (pf)
{
case 61:
Assert.AreEqual(databasevalue1, "00000" + Ledpos1, " Values are not equal ");
Console.WriteLine("Excellent!");
break;
case 76:
Console.WriteLine("Not check as it is random values");
break;
case 164:
Console.WriteLine("TIME!");
break;
case 160:
string todayDate = DateTime.Now.ToString("yyyyMMdd");
Assert.AreEqual(todayDate, databasevalue1, "Both dates are not equal");
break;
case 270:
string tOnedate = DateTime.Now.AddDays(1).ToString("yyyyMMdd");
Assert.AreEqual(tOnedate, databasevalue1, "Both dates are not equal");
break;
default:
Console.WriteLine(pf + pt + len + tableval + databasevalue1);
Assert.AreEqual(tableval, databasevalue1, "Both values are not equal");
break;
}
Console.WriteLine(pf + pt + len + tableval + databasevalue1);
}
Excellent, indeed.
|
Метки: CodeSOD |
Error'd: Breaking News! (or Just a Test?) |
"Hey, Angela! Helio is working!" writes Lawrence R.
"While setting up a new IIS site on Server 2008 within a tightly controlled corporate intranet, I had to remember where the JavaScript setting was in IE 8 in order to test properly," Brian R. wrote, "Thanks Bing for taking me to a page that was very relevant to my issue, and had no other content to distract me from my goal."
"Yes, I think I'll join... wait, what!?" writes Simon.
Yeah, we may need to check into our SEO.
"I was trying to lodge a support ticket to my ISP and, well, it's good to see they're doing their best to prevent spammers," wrote H.
Connor O. writes, "Well, I suppose my email address could serve as my city in a futuristic world."
"Well, looks like I have a long way to go before I ever see 'inbox zero'," wrote Eric
http://thedailywtf.com/articles/breaking-news-or-just-a-test
|
Метки: Error'd |
Guaranteed LOC PITA |
The task Tama set out to accomplish was rather straightforward. One of the clients had a legacy inventory management application, and they needed a simple text field added to an entry form.
Though he'd never seen the code, and the word "legacy" sent chills through his spine, he was confident he could get it done quickly and painlessly. Without hesitation, he downloaded the sources and dug in to acquaint himself with the codebase.
The database migration went easily, but the actual application—a WebForms solution spanning multiple projects and hundreds of files—turned out to be the strangest code Tama had ever seen. The first thing that caught his attention was an abundance of redundant and pointless code. Gems like
bool refreshView = false;
if (refreshView)
{
//...
}
and
if (val == null || val.IsNull)
{
return false;
}
return true;
popped up in nearly every method, bloating them and making Resharper wince in pain as it rendered thousands of warnings. Then there were the methods themselves. Instead of generics, each table column had multiple overloads of various methods with word-for-word identical contents.
The more Tama kept digging, the more he kept cursing at the obviously novice programmer who'd written the application. The code was atrociously unmaintainable, and even the slightest changes required modifying tens of code files across different projects. Instance methods doing nothing but calling out to static ones, multi-line calculations returning a constant value every time—it was as if the previous developer's sole goal was to write as much code as they possibly could, without any regard for proper coding standards or common sense.
Finally, Tama realized what was really going on.
public static string GetXMLString()
{
if(_DefinitionString == "")
{
System.Text.StringBuilder tbf = new System.Text.StringBuilder();
tbf.Append(@"");
_DefinitionString = tbf.ToString();
tbf.Append( @"");
_DefinitionString = tbf.ToString();
tbf.Append( @"");
_DefinitionString = tbf.ToString();
tbf.Append( @"StateId ");
_DefinitionString = tbf.ToString();
tbf.Append( @"State ");
_DefinitionString = tbf.ToString();
tbf.Append( @"Number ");
_DefinitionString = tbf.ToString();
tbf.Append( @"int ");
_DefinitionString = tbf.ToString();
tbf.Append( @"10.0 ");
_DefinitionString = tbf.ToString();
tbf.Append( @" ");
_DefinitionString = tbf.ToString();
tbf.Append( @" ");
_DefinitionString = tbf.ToString();
tbf.Append( @"Y ");
_DefinitionString = tbf.ToString();
tbf.Append( @"Y ");
//...
}
}
SpeedyWare Table Designer, Tama thought to himself.
It was all generated code, or at least used to be at some point, since neither the actual generator nor the sources were anywhere to be found. But why was it so terrible? What possible motivation could lie behind utterly defeating the purpose of a StringBuilder by calling ToString() after each line, among all the other sins and indecencies?
The answer to that question was a quick Google search away. Looking past the various warez sites, Tama managed to find SpeedyWare's webpage, where a large heading proudly boasted:
It seemed that on some fateful day in the past, someone had had the great idea to make a simple app generator. Then Marketing had come along to milk the idea for all it was worth: This tool will write your business apps for you! Marketing promised that it would generate thousands of lines of code, since lines of code were obviously the best metric of effort and quality. Perhaps their management took this claim literally. Perhaps a customer complained that they only received 9,995 lines of code. Whatever the reason, the developers had been forced to bend and twist the generator until it extruded the magical 10,000 lines at any cost.
How well did it work out for the company? Tama scrolled down the webpage, expecting to find testimonials from happy customers announcing how much time the tool saved them. Instead, he found this little blurb:
Technical support for SpeedyWare Table Designer was discontinued on December 31, 2015 as part of our company ceasing operations.
As it turns out, even 10,000 lines of code will not save you if there's not a single good one among them.
|
Метки: Feature Articles |
CodeSOD: A Rusty Link |
Kevin did the freelance thing, developing websites for small businesses. Sometimes, they had their own IT teams that would own the site afterwards, and perform routine maintenance. In those cases, they often dictated their technical standards, like use VB.Net with WebForms.
Kevin took a job, delivered the site, and moved onto the next job. Years later, that company needed some new features added. They called him in to do the work. He saw some surprises in the way the code base had changed.
It was the Contact Us link that drew his attention. The link had a simple job: cause the browser to navigate to a contact form screen. A simple could handle the job. But that tech-savvy boss used this anti-pattern, instead.
First, in the aspx file- the template of the view in WebForms, he added this button:
Contact Us
Then, in the click event handler, he could do this:
Protected Sub lnkContactUs_Click(sender As Object, e As EventArgs) Handles lnkContactUs.Click
Dim strFullURL As String = String.Format("{0}{1}", Config.PublicWebsiteURL, "/?page_id=38")
ClientScript.RegisterStartupScript(Me.GetType(), "Load", String.Format("", strFullURL))
End Sub
This method runs whenever a click of that button in the browser triggers an HTTP request. In the response sent back, it injects a JavaScript file that forces the parent of this window to navigate to the clearly named URL for the contact page- page_id=38. Now, you might think, well, if this link is visible due to a window.open call, this kind of makes sense…, but that doesnt apply here. Instead, its almost certain this code was copy/pasted from StackOverflow without any understanding.
[Advertisement] Release!
is a light card game about software and the people who make it. Play with 2-5 people, or up to 10 with two copies - only $9.95 shipped!
|
Метки: CodeSOD |
All Zipped Up |
Moving to version control is hard. It's a necessary step as a company grows into developing more complex software, with more developers working on the various products, but that doesn't make it any easier. Like all change, it's often delayed far too long, half-assed, and generally resented until everyone's forgotten about the indignity and moved on to complaining about the next improvement.
For Elle's company, the days before Subversion consisted of a few dedicated PCs holding the source code for various customers, to ensure that none of them got mixed up with code for the others. By the time she joined, the company had long since moved to version control, but the source-controlled PCs remained, a curiosity to be laughed over.
Then the budget cuts came, and the team continued to grow. In an effort to reuse the PCs, Hiro, the head of IT, decided to repurpose them as developer workstations. "This is all in version control, right?" he asked nervously. "I can wipe the box?"
"I'm not sure, but I can guess where the repo is if you want me to take a look." Elle knew most of the development was happening in newer codebases, the ones that'd been redone since the bad old days, and she wasn't sure if she even had access rights. Some of the older repositories were locked down weirdly, during a time when paranoia reigned and "security concerns" loomed.
"No, no, it's fine, I'll check myself. Just to be safe." He didn't tell Elle what he found, but the PC was missing the next day when the new guy started, so she figured it must have been there.
Six months later, The Incident happened: their main competitor poached the five most senior staff, offering them better pay and benefits. Elle was jealous; apparently she was ranked number six on the team, and didn't get such a juicy offer herself. Still, she wished them the best of luck before they were frog-marched out to the parking lot by a furious Hiro. Security Concerns. According to the rumor mill, lawyers were brought in to prosecute the other firm for violating their non-compete. Life moved along, now with Elle training the new juniors hired to get the headcount back up.
Two weeks after The Incident, Hiro stopped by Elle's desk. "Hey, remember PegasusCorp?" he asked, naming one of their older clients—one that hadn't required anything from them since before Elle had joined.
"What about them?" asked Elle suspiciously, smelling unpleasant work coming her way.
Sure enough, they wanted some changes. The software needed a facelift. It seemed PegasusCorp's CEO had gotten a copy of Windows 8 and was loving the new "Metro" style. Elle rolled her eyes, but figured, whatever, a few visual changes shouldn't be too hard. She requested access rights to the old repo, checked out the trunk branch, and popped open the folder.
She was faced with a single file: a neat, packaged zip. She blinked. What?
She double-clicked on the zip, and it popped open a password entry field. What?!
She tried the obvious things: password, admin, p@$$w0rd, the name of the company, the name of the product, even Pegasus. No dice. Frowning, she got up and went to ask Hiro.
Hiro's eyes bulged and his face paled. "There's a what?!"
"A password on the zip file. Hey, if you don't know, I'll just ask ..." She trailed off. She was the most senior dev now, and she had no idea what the password could be. If the head of the department didn't know, that meant ...
"Just call? Please? I'm sure they'll be reasonable," begged Hiro.
Elle groaned as she trudged back to her desk, digging out the company off-hours cellphone directory. Whose bright idea was it to password-protect the damn source code?!
She called the first of the five, and only got as far as, "I'm calling from CompanyName" before she was met with a furious expletive followed by a dial tone. She stared at the phone in horror. What had Hiro done to the guy?!
The second person was more forthcoming, if not more helpful. "No way! Not unless you call off your lawyers. I had a great thing lined up, you ruined it, and now you come begging for help?"
Elle had a sinking feeling about her remaining prospects, but had to keep calling. The third person had changed their number without updating the roster. The fourth was apologetic, but simply didn't know what the password was. The fifth just laughed and laughed until an unnerved Elle hung up the phone herself.
God, I need a beer, she thought, lowering her head into her hands. How hard could it be to crack?
Elle grabbed a dictionary file of English words and threw together a batch file with a handful of basic fuzz techniques: backwards, with digits appended, in l33t, etc. She let it run overnight, confident that she'd have an answer—and source code—in the morning.
Beer time.
But she didn't have the password in the morning. Or the next morning, or the next. By the time she left Friday night, she was seriously worried for her job. Hiro looked miserable and frantic, and her script was almost out of words to try.
Monday morning, as she was rubbing the sleep from her eyes, Elle was startled by a loud thud. She lowered her hands, staring at the center-most table where ...
Is that Hiro? she wondered. With a ... keg?!
"Whoever can crack the PegasusCorp password gets this keg!" Hiro announced to the sleepy techs. "Have at ye!"
By the end of the day, the source was revealed: resigning technician number five had been a huge Star Wars fan, and the password had merely been JengoFett. The keg was shared around the office, and come Tuesday morning, Elle was able to do the facelifts required.
Meanwhile, the lawsuit backfired. The resigning staff got their jobs, and Elle's company had to pay for the suit, compensation for impounding their company vehicles, and salary up until their official resignation date.
And life moved on, as it always does.
|
Метки: Feature Articles |
CodeSOD: Grumpy Cat |
At the end of the lecture session, students immediately started packing up their laptops to race across campus for their next class. Andrews professor droned on, anyway, describing their assignment. Ive provided parser code, he said, so you can just download the datafile and use it.
He had more to say, but no one was paying attention. Perhaps he even had a correction to the assignment- because when Andrew went to download the data file for the assignment 404ed.
Andrew emailed the professor, but figured- Hey, I can get started. Ill just read the parser code to understand the file format.
String line = br.readLine();
for (int i=0; icode>
The parsing code itself was an overly complicated reinvention of String.split with absolutely no error handling. That was enough to make Andrew cringe, but its hardly the worst code you could have. No, the real bad code was the code that read from the file.
You might be thinking to yourself, Well, Java does make file I/O a bit complicated, but you just link a FileInputStream with a BufferedReader, or even save yourself all that trouble and use the very convenient FileReader.
Now, I dont want to put words in this professors mouth, but I think they would point out that a FileReader reinvents a wheel thats already on your OS. Why go through all this trouble to open a file and read from it if you already have a program on your OS that can open and read files?
List command = new ArrayList();
command.add("cat");
command.add(filename);
ProcessBuilder builder = new ProcessBuilder(command);
builder.redirectErrorStream(true);
final Process process = builder.start();
InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
// discard the first line
String line = br.readLine();
for (int i=0; icode>
Why not open the cat command? I mean, sure, now youre tied to a system with cat installed, losing the cross-platform benefits of Java. I hope none of these students are using Windows. Then theres the overhead of launching a new process. Also, process.waitFor is a blocking call that doesnt terminate until the called program terminates, which means a bad input can hang the application.
Its worth noting that this code wasnt merely written for this assignment, either. In fact, this code started life as part of a research project conducted by multiple members of the CS departments faculty. Using this parser was not an optional part of the project, either- use of this code was required.
|
Метки: CodeSOD |
Error'd: Let's Do the Timestamp Again! |
"I am fairly certain that the data within this .csv file contains the secret of time travel," Merrick writes.
Darrell T. wrote, "We never met you, and we miss you!"
"It's a good thing that they included significant digits. Anything less than 4.4096915 is clearly unacceptable," wrote Tyler.
Brent W. writes, "Not even remotely sure what popped this error/warning/whatever up. The OK/Ignore don't help much either. Bottom line - my Azure was broke."
"Looking for proof of global warming? Check out the below," wrote Geoff
Alexander S. wrotek, "German Outlook pops up this message: "During archiving errors ocurred. A detailed report is placed in folder 'Deleted Elements'"
"Looks like cars aren't the only things that crash in racing arcade games," writes David.
[Advertisement] Release!
is a light card game about software and the people who make it. Play with 2-5 people, or up to 10 with two copies - only $9.95 shipped!
http://thedailywtf.com/articles/let-s-do-the-timestamp-again
|
Метки: Error'd |
.gitignorant |
Brent, who had started at JavaChip in QA several years ago, was tapped for real work with the core development team. On the day of his transfer, he gathered his things from his desk in a cardboard box, told his teammates in QA that hed continue to see them for D&D at lunch, and trekked down the hall to the larger office.
After finding his new desk, he went to find Karla, his team lead. As it turned out, Karla had called in sick, but she had sent Brent an email from home. Get settled in, she wrote. Our repos on the company git server. Make sure you have Maven and IntelliJ installed on your machine. Everything else is in the README.md file.

Dutifully, Brent pulled down the repo. The size counter crept up. 10 MB & 20 & 30 &
He had just pulled a 100MB repository onto his computer.
There was no conceivable way the repo should be that large.
First, he imported the project into IntelliJ and built it with Maven, making sure there wasnt anything wrong before he started tinkering. With no compiler errors or warnings, he opened WinDirStat and pointed it to the repo. The code relied on some hefty third-party libraries, but an initial scan revealed that those libraries didnt take up more than 10MB. Including company-owned code, he had accounted for about 15MB of the 100MB repository size.
Brent saw the bigger issue. In chrome red, faceted so small each file was about a pixel in size, were over 85MB of log files. They were generated by Maven and other parts of their compiler chain, written each time the project was built.
Well, this should be easy, Brent thought. Ill just add the log directories to .gitignore. Not bad for my first day on the team.
Brent opened IntelliJ and dug around for a .gitignore file. Only there wasnt one. He checked the root directory, in /src and other code directories, even a few libraries to make sure it hadnt been put somewhere unusual. He even made sure IntelliJ wasnt hiding system files, which .gitignore was sometimes treated as. There simply wasnt one in the repo at all.
Fair enough, he thought. Ill just write one. Brent added a new .gitignore file in the root, put the log directories (and a few other suspect paths) into it, and submitted his first code change.
Brent was feeling confident after his commit. He began rummaging through the repository, getting a feel for the codebase.
However, sometime after lunch, Brent heard phlegmatic coughing from the entrance. Karla, the team lead, had come in on her sick day, and she was heading straight for Brents desk.
Brent, cough, we really need you to revert that commit.
Why? I just added a .gitignore file.
Right, thats the problem. None of us here ever check in the .gitignore file.
Dont you want to configure your repo properly?
Were all pretty new to git, to be honest, but we had a big mess with conflicts when people were adding their own entries to the gitignore. Just revert and Ill take care of it.
Reluctantly, Brent reverted his change.
An hour later, he received an auto-generated email from the company git server: Karla had checked in a commit to the repo. The only change was to add a .gitignore file in the root directory. Thinking that she just preferred to write one herself, Brent opened the file in IntelliJ:
# Exclude gitignore from git
.gitignore
Brent checked the email again. The commit message was prevents .gitignore from being added to the repo. Karla had tried to get the repo to ignore .gitignore & using .gitignore. But for the rule to work, .gitignore needed to be added to the repo. Worse, Karla told him to keep his hands off the file, meaning nothing else could ever be ignored on the repository other than that file.
For the remainder of his first day on core team, Brents head spun. On QA, he sometimes wondered how things worked on the core team. But sometimes its not worth knowing how the sausage is .gitignore-ed.
|
Метки: Feature Articles |
CodeSOD: Non-Threading |
Reader Tyler shares this outstanding example of thread evasion:
Apparently they needed a way to delay current execution but still process window messages, and running it in a separate Thread and calling Sleep() would have been too hard ... Thankfully this function isn't actually being used anymore, and has been canned from the repo.
Thankfully, indeed. Come for the honest commenting, stay for the funky definition of one second, the adding of seconds that I think is supposed to represent real time ticking by as the function runs, the countup (countdowns are lame), and the invocation of Application.DoEvents(), a holdover from Visual Basic. Seeing as this is a VB.NET app, it's probably not the best way to allow other processes and events to run, but there are differing opinions on this.
''NEVER USE THIS FUNCTION, it is full of evil and it will come to haunt you....
Public Shared Sub Delay(ByVal dblSecs As Double)
Const OneSec As Double = 1.0# / (1440.0# * 60.0#)
Dim dblWaitTil As Date
Now.AddSeconds(OneSec)
dblWaitTil = Now.AddSeconds(OneSec).AddSeconds(dblSecs)
Do Until Now > dblWaitTil
Application.DoEvents() ' Allow windows messages to be processed
Loop
End Sub
|
Метки: CodeSOD |
Re-Relational |
Given the rise of the internet in the mid 1990's, various events and companies led up to Adobe releasing Flash. Not to be out done, in the mid noughts, Microsoft created their own version called Silverlight. Somewhere down the road, Facebook, Instagram and others put forth React. These can sit on top of a webservice, like, for example, WCF to make it easier for web-facing programs to call home to interact with back-end applications to do useful things like display videos of cats being, well, cats. Occasionally, folks even attempt to use these tools to provide access to business applications.
Some time back, Fred became a hired-gun/consultant/architect to a small financial company to help them replace a dying 150K LOC Silverlight UI with a React front-end, and the underlying WCF API (named Rest.Services for some reason). This allegedly trivial task was budgeted to take three months. Ten months down the road, Silverlight and the underlying code base were way ahead on points while the battle raged on. Eventually, management acquiesced and allowed the entire UI to be rewritten from scratch. The back-end, however...
The application was a financial budgeting/reporting set-up. While it served its purpose from a business perspective, under the hood was a Cluster-O-WTF™ that simply would not cooperate with any attempts at change. Additionally, over time, the system became more and more obstinate about providing answers to queries. One morning, Fred noticed that the overnight updates were still running and the CPUs were pegged at 100%. A quick spelunk into the SQL showed why.
Like every other financial system, there were multiple tables containing related data. One might expect each table to have a PK and the relationship-table to have a bunch of FKs.
One would be wrong.
Fred found multiple sets of tables, each with the following design pattern.
create table Aux_1 as (
pk_id bigint,
field1 varchar(10),
...
);
create table Aux_2 as (
pk_id bigint,
field2 varchar(10),
...
);
create table RelationshipAux1Aux2 as (
pk_id bigint,
keys varchar2(300),
...
)
In the keys field, there were delimited FK references to the other related tables. These were hand-crafted relationships in a relational database!
The system would delete the old auxiliary data records and insert new ones, creating new corresponding relationship records in the process. Without any sort of referential integrity, the old relationship records would be orphaned.
With no home-grown concept of cascading deletes to match this set-up, in several of the relationship tables, more than 18 million of 20 million rows were orphaned. Naturally, since the developers of this brillance didn't know to use FKs, they certainly didn't know to use built-in indices either.
Of course, that meant that in order to find anything, the database needed to grind through and pick apart tens of millions of orphaned relationship rows in addition to the few million rows of actual data. After several years of this Garbage Accumulation™, it reached critical mass and the system just couldn't grind through enough data to finish.
A careful delete got the system running again. Fred now spends his days adding indices and attempting to replace the home-grown FK mechanism with actual FKs.
|
Метки: Feature Articles |
CodeSOD: Not the Shortest Shortener |
Going through TDWTF inbox, Ive built a sort of mental taxonomy of bad code. For example, theres the kingdom of Tempus Malum: home-brew date manipulation functions, a rather profligate branch of bad code. Or the Order of Linguan Ignorans- bad code developed out of a complete ignorance of the available language features.
Theres another category that I always consider a treat. Its related to Linguan Ignorans, but also borrows from Quaesto Ignorat (ignorant of the problem being solved): Filo Annexa, or Knotted String, also known as String All the Things!
Which brings us to todays C# code, from Aaron.
public string ShortenFloatString(float valueToShorten)
{
string xString;
string xString2 = "";
xString = valueToShorten.ToString();
if (xString.IndexOf('.') > -1)
xString2 = xString.Substring(xString.IndexOf('.'));
if (xString2.Length > 3)
xString2 = xString2.Substring(0, 3);
if (xString.IndexOf('.') > -1)
xString = xString.Substring(0, xString.IndexOf('.'));
if(xString2.Length > 2)
{
if(xString2[2] == '9')
{
int num = int.Parse(xString2[1].ToString());
if (num <= 9)
{
num++;
xString2 = "." + num.ToString();
}
else
{
num = 0;
xString2 = "." + num.ToString();
int firstNum = int.Parse(xString);
firstNum++;
xString = firstNum.ToString();
}
}
}
return (xString + xString2);
}
Ive never had to spend so much time looking at a rounding function to confirm that it is, in fact, a rounding function. Well, sort of. It doesnt enforce any rounding rule that Ive ever heard of.
Like all bad code, it eschews the built in functions for solving this problem. But it takes it to another level, by doing all the rounding through string manipulation- at least as much as it can. Strings, of course, cant be effectively rounded, so of course theres a moment where we need to int.Parse bits of the strings back into numbers so that we can actually do rounding.
Bonus points are awarded for excellent variable names, and also for using Substring instead of Split, which is also a built-in method.
[Advertisement]
Atalasoft’s imaging SDKs come with APIs & pre-built controls for web viewing, browser scanning, annotating, & OCR/barcode capture. Try it for 30 days with included support.
|
Метки: CodeSOD |
Error'd: Workweek Hustle Shuffle |
While my co-worker JR may have gotten the most steps, I still came out on top!
"Not sure if I'm interested in their audience targeting rule or not," writes Tony P.
Paul N. wrote, "It's nice to see that it's definitely not spam, because I'm... NOT_SIGNED_UP?!"
"Gotta love Siri's personality -- she is kidding, right?" writes Sean.
Autin wrote, "I guess it does take a lot of memory to do laundry."
"Anyone fancy some used envelopes? Only need to cover up the old address and steam off the stamp, and they're just like new!" Dave L. writes.
Phil writes, "Wow! I'm really looking forward to getting 3.99TB of disk space back on my 60GB drive."
|
Метки: Error'd |
Editor's Soapbox: Programming is Hard |
A bit ago, I popped into an Explain Like Im 5 thread to give my version of the differences between C, C++, Objective-C and C#. In true Reddit fashion, I had the requisite no five year old could understand this comments and similar noise. One thing that leapt out to me was that a few commenters objected to this statement: Programming is hard.
The most thorough objection read like this:
Programming is not hard(such a discouraging label). If you learned a written language, you can learn a computer language. The only thing difficult about programming is mastering the art of learning itself, which is what separates the soon-to-be obsoletes from the ever-evolving masters of the trade. Good programming, at its heart, is all about keeping up with the technologies of the industry. The fundamentals themselves are easy peasy.
/u/possiblywithdynamite
Now, I dont want to pick on /u/possiblywithdynamite specifically, although I do want to point out that keeping up with the technologies of the industry is the exact opposite of good programming. Id also like to add that a positive attitude towards life-long learning is neccessary to be successful in general, not just programming. I dont want to get sidetracked by those points.
Instead, I want to focus on a few other related things:

Now, what do we mean by hard? Theres the obvious definition: you have to do a lot of work to succeed. Time and energy have to be invested. Or, its a task that requires great skill and talent. These are our common ideas of hard, and theyre more what I meant in my original comment, but I want to highlight an alternate definition of hard.
A task is hard when there are more ways to screw it up than to do it correctly. If youre blundering through the world of programming and software development, there are a lot more ways to be wrong than there are to be right. Most of what we do on this site is inventory all the ways in which people can mangle basic tasks. Think about how many CodeSODs work but are also terrible.
When we talk about learning a skill—be it programming, a musical instrument, writing, improvised comedy, etc.—what were really talking about is building habits, patterns, and practices that help us do things correctly more often than we do them wrong. And how do we do that? Usually by screwing up a bunch of times. Improvisers call this getting your reps, musicians call it going to the woodshed (because your playing is so awful you need to be away from the house so as not to disturb your family). As the old saying goes, An expert is someone who has failed more times than a novice has even tried. The important thing is that you can read up on best practices and listen to experts, but if you want to get good at something hard, youre going to need to screw up a bunch. We learn best from failures.
Okay, so programming is hard, and we can deal with its inherent difficulty by building skill. But should programming be hard? After all, havent we spent the past few decades finding ways to make programming easier?
Well, I dont want to imply that we should make programming hard just for the sake of making it hard. I dont want to give the impression that we should all throw out our garbage collectors and our application frameworks and start making hand-crafted artisanal Assembly. Keep in mind how I defined hard: lots of ways to screw up, but only a few ways to succeed. That means difficulty and degrees of freedom are related. The more options and flexibility you have, the harder everything has to be.
Lets shift gears. I didnt learn to ride a bike until relatively late compared to my peers. As was standard at the time, my parents bought me a small bike and put training wheels on it. I was just awful at it. Id fall over even with the training wheels on. I hated it, but for a kid too young to drive in the yes, you can go out by yourself era of the 80s, being able to ride a bike was a freedom I desperately wanted.
At some point, my parents decided I was too old for training wheels, so they took them off. Sink or swim, my dad said. And it was amazing, because I almost instantly got better. Within a few days, I was riding like I was born to it. Cheerfully running errands down to the corner shop, riding back and forth to school, it was everything I wanted.
Little did I know that training wheels are considered harmful.
To learn to bike, you must solve two problems: the pedaling problem and the balance problem. Training wheels only solve the pedaling problem—that is, the easy one. Learning to balance on a bike is much more difficult, and a “training” tool that eliminates the need to balance is worse than beside the point.
Which brings us to languages like Scratch and Blockly. Now, these are educational toy languages with syntax training wheels. You cannot write a syntactically incorrect program in these languages. You simply arent permitted by the editor. I am not a child educator, so I cant speak to how effective it is as an educational tool. But these sorts of visual languages arent limited to educational languages. Theres SSIS and Windows Workflow. MacOS has Automator. I still see Pointy-Haired Bosses looking for UML diagrams before anybody writes a line of code.
Its not just visual languages. Think about simple languages like JavaScript, PHP, VB. Think about the worst sin Microsoft ever committed: putting a full-featured IDE in their Office suite. In all these cases, the languages attempt to constrain your options, provide faster feedback, and make a best guess about what youre trying to do. They try to steer you around some of the common mistakes by eschewing types, simplifying syntax, and cutting back on language features.
I am not trying to say that these languages are bad. Im not trying to say that they dont have a place. I definitely dont want this to sound like Ivory Tower Elitism. Thats not the point I want to make. What I am saying is that simplifying languages by helping the users solve the easy problems and not the hard ones is a mistake.
Beyond that point, programming needs to be hard. Lets focus on syntax. I remember in my first C++ class, I spent days—literal days— hunting for a semicolon. Thats a huge hurdle for a beginner, struggling with no progress and unhelpful feedback. Languages that simplify their syntax, that forgive missing semicolons, seem like a pretty natural way to bypass that difficult hump.
Heres the problem, though: once you understand how to think syntactically, syntax itself isnt really a bother. Theres a hump, but the line flattens off quickly. Learning a new language syntax ceases to be a challenge. By taking the training wheels off, youre going to fail more often—and failing is how you learn. You can find more things to do, and more ways to do them, without having to start over—because you built strong habits. More than that, you cant avoid the importance of syntax. At some point, your application will need to interact with data, and that data will need to be structured. Were often going to wrap that structure in some kind of syntax.
This is just an illustration of the general problem. Making a hard task easier means giving up flexibility and freedom. Our tools become less expressive, and less powerful, as a result. The users of those tools become less skilled. Theyre less able to navigate past the bad solutions because the bad solutions arent even presented to them.
Now, if this ended here, itd be in danger of being a grognard-screed. My powerful-but-opaque-and-incomprehensible-tooling isnt the problem, you just need to git gud. I emphatically do not want this to be read that way, because I strongly believe that programming and software development need to be accessible. In the wilderness of incorrect solutions, someone with little or no experience should be able muddle through and see progress towards a goal. I dont want the IT field to be a priesthood of experts who require initiates to undergo arcane rituals before they can be considered worthy.
Hard means its easy to screw up. Accessible means that there are some sign-posts that well help keep you on the path. These are not mutually exclusive. Off the top of my head, here are a few things that we can keep in mind when designing languages, tools, and tutorials:
Also, dont lie. Programming is hard. That might be a discouraging label, but its an accurate label. Whats more discouraging, going: This is hard, but heres how you can get started, or This is easy (but its actually hard and youre going to fail more often than you succeed)?
Phew. This turned into a much longer piece than I imagined it would be. As developers, were constantly building tools for others to use: other developers, our end users, and maybe even aspiring developers. Understanding what makes something hard, and why its sometimes good for things to be hard, is important. Recognizing that we can deal with difficult problems by building skills, by practicing and honing our craft, means that we also have to recognize the value of being skilled.
[Advertisement] Release!
is a light card game about software and the people who make it. Play with 2-5 people, or up to 10 with two copies - only $9.95 shipped!
|
Метки: Editor's Soapbox |
CodeSOD: Every Possible Case |
From reader Frank comes this delightful Java-flavored head-scratcher:
This is from the main request-handling method for a webservice. Counting all of its interesting features is left as an exercise for the reader, but the request data is never modified and the error handling in both catch blocks is the same .... The kicker is that
doCalculation()has all its logic insidetry {} catch (Exception ex)anyway.
Response response = new Response();
try {
if (request.includeFooFlag()) {
response = doCalculation(request);
}
if (! request.includeFooFlag()) {
long fooCount = getCountOfFoo();
if (fooCount == 0) {
response = doCalculation(request);
} else {
try {
response = doCalculation(request);
} catch (Exception ex) {
response = createErrorResponse("Error details");
return response;
}
}
}
return response
} catch (Exception ex) {
response = createErrorResponse("Error details");
return response;
}
return response;
[Advertisement] Manage IT infrastructure as code across all environments with Puppet. Puppet Enterprise now offers more control and insight, with role-based access control, activity logging and all-new Puppet Apps. Start your free trial today!
|
Метки: CodeSOD |