Coded Smorgasbord: Properly Handled PHP |
Its tempting to pick on PHP, because PHP is a terribly designed language. At the same time, theres an air of snobbery and elitism in our mockery of PHP. I mean, half the Web runs on PHP, so how bad can it be? These examples could easily have been written in nearly any other language, and theyd be just as bad in those languages too. Is it fair to single out PHP? Perhaps not, but each of these examples does nothing- or nearly nothing- which may very well be PHPs greatest asset.
As a case in point, Ethan inherited some code. It needs to count how many sub-directories there are in a certain directory.
$dir = opendir("/var/www/media/docs/");
$nCount = 0;
while($imagename = readdir($dir)) {
if (strlen($imagename)>2 && is_dir("/var/www/media/docs/" . $imagename)) {
$nCount++;
$foldernames[] = $imagename;
}
}
closedir($dir);
We could pick on hard-coded strings and misleading, but for the most part, this code doesnt seem too unreasonable. That is, until you look at the expression strlen($imagename)>2 in that if condition. What on Earth is going on there? Well, Ethan wondered the same thing. After some research, he managed to figure it out- there was a folder called HR that the original developer didnt want indexed by this code. If you dont think about it, this is much more efficient than an equality test, because this only has to look at every character in one string, not two.
Well, that code definitely does something. Is there some code that doesnt do anything? Well, what about some code that doesnt do anything useful? Betty learned about this error handling convention from a very expensive contractor.
try {
// code
} catch (Exception $e) {
throw new Exception($e->getMessage());
}
At least we know every exception is being handled… except the last one.
That still does too much. Is there any PHP code thats even safer- and by safer, I mean more useless? Well, what about this shopping cart system David C found?
$pids = array();
foreach ($scratch_products as $product_data) {
$pids[] = $product_data['productid'];
}
unset($pids);
Now theres the stuff. Create the array, populate it, and destroy it all in three lines. It would have worked great, too, if not for the fact that some code elsewhere in the program expected $pids to exist.
[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!
|
Метки: Coded Smorgasbord |
Logjam |
Steven worked for Integrated Machinations, a company that made huge machines to sell to manufacturers so they could actually manufacture stuff. He didn't build the machines, that would require hard physical labor. Instead, he wrote computer programs that interfaced with the machines from the comfort of the air-conditioned office. One such program was a diagnostic app used to log the performance of Integrated Machinations products. The machines didn't break down often, but when they did, logging was very important. Customers wouldn't be in a mood to hear that IM didn't know why the equipment they dropped fat stacks of cash on failed.
Steven also had a subordinate named Thomas, who was foist upon Steven in an effort to expand the small development team. Steven could have easily handled everything himself, but Thomas needed something to do so he was given the simplest part of the diagnostic app - the downloader. Steven's code handled the statistical compiling, number-crunching, and fancy chart-making aspects of the application. All Thomas had to do was make the piece that downloaded the raw files from the machines to pass back.
Thomas spent two months on something that would have taken Steven a week tops. It worked in their test environment, but Steven wanted to code review it went to production. Before he could, the higher-ups informed him there was no time. The logging and downloading system was installed and began to do its thing.
Much to Steven's pleasant surprise, the downloader piece worked in the real world. Thomas had it set up to run every minute from Crontab on every machine their pilot client had. It passed back what the compiler needed in XML format and they had neatly-displayed diagnostic stats to show. This went on for a week, until it didn't.
Steven came in that Monday to find that nothing had been downloaded over the weekend. As soon as Thomas meandered in, unshaven and bleary-eyed, he instructed him to check on the downloader. "Sure, if I can fight off this hangover long enough. Are you sure your stuff isn't broken??" Thomas replied, half joking, half trying not to pass out.
Two hours passed, half of which Thomas spent in the bathroom. He finally came back to Steven's office to report, "Everything is back to normal! We lost all the logs from the weekend, but who works on the weekend anyway?" He quickly disappeared without further explanation.
So began a repeating cycle of the downloader crashing, Thomas coming to work hung over, then fixing it without explanation. The Thomas problem got resolved before the downloader problem. He was relieved from his employment at Integrated Machinations after his sixth "no-call, no-show". This left Steven to support the downloader the next time it went down. It was completely undocumented, so he had to dig in.
He found the problem was with the log file itself, which had bad XML for some reason. Since XML has a rigorously specified "Parse or Die!" standard, and Thomas wasn't much for writing exception handlers, the next time the downloader ran, it would read in the XML file, get a parse error, and die. It was at this point Thomas would have to delete the XML file, restart the downloader, and things would get back to normal.
Digging in further, he found every time the downloader ran, it read and parsed the entire log file, then manipulated the parse tree and added a new
...
There was quite a bit of code dedicated to this rather complex and intricate process but there were no obvious problems with the code itself. When it ran, it worked. But if you just left the thing running from Cron, then sooner or later, something would go wrong and the XML file on disk would get corrupted. Steven never could figure out what was causing the file corruption, and it wasn't worth investigating. He tore out all the logging code and replaced it with three lines:
# Name the file after today's date
# >> opens the file for append. Linux *always* gets this right.
open FILE, ">> $log_dir/$date.log";
# Look Ma! No XML!
print FILE "$date $time $downloaded_filename\n";
# Delete any files that are more than 3 days old
unlink grep { -M > 3 } <$log_dir/*.log>;
From there on, the downloader never failed again and the scourge of Thomas had been put to rest.
[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!
|
Метки: Feature Articles |
Error'd: Surprise! |
"In life, there are good surprises and bad surprises, but Microsoft fails to differentiate," writes Rob
"Now I know how people from New Foundland feel," writes Chris B.
Austin S. writes, "Who knew that doing squats was the secret to bypassing *nix security?"
Pawel wrote, "I think WolframAlpha should fix its own problems before generating new ones."
"lb0? I'll take one. Hell, I'll take ten!" writes Will K.
Rick R. wrote, "What happened with Detroit? Did they run around the bases in the wrong direction?"
Alex F. wrote, "Sometimes I guess string casting is like throwing spaghetti and hoping that it sticks."
|
Метки: Error'd |
CodeSOD: As The World Ternaries |
Ah, the ternary operator. At their worst theyre a way to obfuscate your code. At their best, theyre a lovely short-hand.
For example, you might use the ternary operator to validate the inputs of a function or handle a flag.
Adam Spofford found this creative use of the ternary operator in a game hes developing for:
this.worldUuid = builder.worldId == null ? null : builder.worldId;
this.position = builder.position == null ? null : builder.position;
this.rotation = builder.rotation == null ? null : builder.rotation;
this.scale = builder.scale == null ? null : builder.scale;
this.worldUuid = builder.worldId;
this.position = builder.position;
this.rotation = builder.rotation;
this.scale = builder.scale;
Curious about how this specific block came to be, Adam poked through the Git history. For starters, the previous version of the code was the last four lines- the sane lines. According to git blame, the project lead added the four ternary lines, with a git comment that simply read: Constructing world details. That explains everything.
|
Метки: CodeSOD |
Cache Congestion |
Recently, we featured the story of Alex, who worked in a little beach town trying to get seasonal work. But Alex isn't the only one with a job that depended entirely on the time of year.
For most seasonal work in IT, it's the server load that varies. Poor developers can get away with inefficient processes for three quarters of a year, only to have it bite them with a vengeance once the right season rolls around. Patrick, a Ruby developer, joined an educational technology company at the height of revision season. Their product, which consisted of two C#/Xamarin cross-platform mobile apps and one Ruby/Rails back-end server, was receiving its highest possible traffic rates. On his first day at the office, the entire tech team was called into a meeting with the CEO, Gregory, to address the problem.
Last year, the dev team had been at a similar meeting, facing similar slowness. Their verdict: there was nothing for it but to rewrite the app. The company had, surprisingly, gone in for it, giving them 6 months with no task but to refactor the app so they'd never face this kind of slowdown again. Now that the busy season had returned, Gregory was furious, and rightly so. The app was no faster than it had been last year.
"I don't want to yell at anyone," boomed Gregory, "but we spent 6 months rewriting, not adding any new features—and now, if anything, the app is slower than it was before! I'm not going to tell you how to do your jobs, because I don't know. But I need you to figure out how to get things faster, and I need you to figure it out in the next 2 weeks."
After he left, the devs sat around brainstorming the source of the problem.
"It's Xamarin," said Diego, the junior iOS Dev. "It's hopelessly unperformant. We need to rewrite the apps in Swift."
"And lose our Android customer base?" responded Juan, the senior Mobile Dev. "The problem isn't Xamarin, it's the architecture of the local database leading to locking problems. All we have to do is rewrite that from scratch. It'll only take a month or so."
"But exam season will be over in a month. We only have two weeks!" cried Rick, the increasingly fraught tech lead.
Patrick piped up, hoping against hope that he could cut through the tangled knot of bull and blame. "Could it be a problem with the back end?"
"Nah, the back end's solid," came the unanimous reply.
When they were kicked out of the meeting room, lacking a plan of action and more panicked than ever, Patrick sidled up to Rick. "What would you like me to work on? I'm a back end dev, but it sounds like it's the front end that needs all the work."
"Just spend a couple of weeks getting to grips with the codebase," Rick replied. "Once exam season is over we'll be doing some big rewrites, so the more you know the code the better."
So Patrick went back to his desk, put his head down, and started combing through the code.
This is a waste of time, he told himself. They said it was solid. Well, maybe I'll find something, like some inefficient sort.
At first, he was irritated by the lack of consistent indention. It was an unholy mess, mixing tabs, two spaces, and four spaces liberally. This seriously needs a linter, he thought to himself.
He tried to focus on the functionality, but even that was suspect. Whoever had written the backend clearly hadn't known much about the Rails framework. They'd built in lots of their own "smart" solutions for problems that Rails already solved. There was a test suite, but it had patchy coverage at best. With no CI in place, lots of the tests were failing, and had clearly been failing for over a year.
At least I found something to do, Patrick told himself, rolling up his sleeves.
While the mobile devs worked on rebuilding the apps, Patrick started fixing the tests. They were already using Github, so it was easy to hook up Travis CI so that code couldn't be merged until the tests passed. He adding Rubocop to detect and correct style inconsistencies, and set about tidying the codebase. He found that the tests took a surprisingly long time to run, but he didn't think much of it until Rick called him over.
"Do you know anything about Elastic Beanstalk auto-scaling? Every time we make a deployment to production, it goes a bit haywire. I've been looking at the instance health, and they're all pushing 100% CPU. I think something's failing out, but I'm not sure what."
"That's odd," Patrick said. "How many instances are there in production?"
"About 15."
Very odd. 15 beefy VMs, all running at > 90% CPU? On closer inspection, they were all working furiously, even during the middle of the night when no one was using the app.
After half a day of doing nothing but tracing the flow, Patrick found an undocumented admin webpage tacked onto the API that provided a ton of statistics about something called Delayed Job. Further research revealed it to be a daemon-based async job runner that had a couple of instances running on every web server VM. The stats page showed how many jobs there were in the backlog—in this case, about half a million of them, and increasing by the second.
How can that work? thought Patrick. At peak times, the only thing this does is make a few jobs per seccond to denormalising data. Those should take a fraction of a second to run. There's no way the queue should ever grow this big!
He reported back to Rick, frowning. "I think I've found the source of the CPU issue," he said, pointing at the Delayed Job queue. "All server resources are being chewed up by this massive queue. Are you sure this has nothing to do with the apps being slow? If it weren't for these background jobs, the server would be much more performant."
"No way," replied Rick. "That might be a contributing factor, but the problem is definitely with the apps. We're nearly finished rewriting the local database layer, you'll see real speedups then. See if you can find out why these jobs are running so slowly in the meantime, though. It's not like it'll hurt."
Skeptical, Patrick returned to his desk and went hunting for the cause of the problem. It didn't take long. Near the top of most of the models was a line like this: include CachedModel. This was Ruby's module mixin syntax; this CachedModel mixin was mixed into just about every model, forming a sort of core backbone for the data layer. CachedModel was a module that looked like this:
module CachedModel
extend ActiveSupport::Concern
included do
after_save :delete_cache
after_destroy :delete_cache
end
# snip
def delete_cache
Rails.cache.delete_matched("#{self.class}/#{cache_id}/*")
Rails.cache.delete_matched("#{self.class}/index/*")
# snip
end
end
Every time a model was saved or deleted, the delete_cache method was called. This method performed a wildcard string search on every key in the cache (ElastiCache in staging and production, flat files in dev and test), deleting strings that matched. And of course, the model saved after every CREATE or INSERT statement, and was removed on every DELETE. That added up to a lot of delete_cache calls.
As an experiment, Patrick cleared out the delete_cache method and ran the test suite. He did a double-take. Did I screw it up? he wondered, and ran the tests again. The result stood: what had once taken 2 minutes on the CI server now completed in 11 seconds.
Why the hell were they using such a monumentally non-performant cache clearing method?! he wondered. Morbidly curious, he looked for where the cache was written to and read using this pattern of key strings and found ... that it wasn't. The caching mechanism had been changed 6 months previously, during the big rewrite. This post-save callback trawled painfully slowly through every key in the cache and never found anything.
Patrick quietly added a pull request to delete the CachedModel module and every reference to it. Once deployed to production, the 15 servers breezed through the backlog processing jobs over the weekend, and then auto-scaled down to a mere 3 instances: 2 comfortably handling the traffic, with another to avoid lag in scaling. There was a noticeable impact on performance of the apps now that more resources were available, as the server endpoints were significantly more responsive. Or at least, the impact was noticeable to Patrick. The rest of the tech team were too busy trying to work out why their ground-up rewrite of the app database layer was benchmarking slower than the original. Before they figured it out, exam season was over for another year, and performance stopped being a priority.
|
Метки: Feature Articles |
CodeSOD: Exceptional Condition |
This is part of a home-grown transpiler…, Adam wrote. I could stop there, but this particular transpiler has a… unique way of deciding if it should handle module imports.
Given a file, this Groovy code will check each line of the file to see if it includes an import line, and then return true or false, as appropriate.
private static boolean shouldConvert(String headerPath) {
File headerFile = new File(headerPath)
try {
headerFile.eachLine {
if (it.contains("MODULE_INCLUDE core.") ||
it.contains("import core.")) {
throw new Exception("true, we found a good import")
}
}
// Never found a valid MODULE_INCLUDE or import; don't convert it
throw new Exception("false, no good import found")
} catch (Exception e) {
return e.getMessage().contains("true")
}
}
Now, Im no Groovy expert, but I suspect that theres probably an easier way to return a boolean value without throwing exceptions and checking the content of the exception message.
Adam adds: I especially envisioned changing the second exception to something like false, it is true that we didnt find a good import.
|
Метки: CodeSOD |
Announcements: Sponsor Announcement: Atalasoft |
Lets take a moment to talk about documents. I once worked on an application that needed to generate some documents for Sarbanes-Oxley compliance, and without confessing to too much of a WTF, lets just say it involved SQL Server Reporting Services, SharePoint, and some rather cryptic web service calls that Im almost certain have stopped working in the years since I built it. The solution belongs here.
I bring this up, because Im happy to announce a new sponsor here at TDWTF: Atalasoft, which would have kept me from writing that awkward solution.

Atalasoft makes libraries for working with documents from your .NET applications. There are SDKs for manipulating images, working with PDFs, and mobile SDKs for doing document capture on iOS or Android devices, and WingScan provides interaction with TWAIN scanners right from inside of a web browser. Their products provide zero-footprint document viewing, easy interfaces for constructing and capturing documents, and come with top-tier support for helping you get your application built.
This sponsorship coincides with their latest release, which partners with Abbyys FineReader to add OCR support, the ability to interact with Office documents without Office installed, new PDF compression options, and a variety of improvements to their already excellent controls and SDKs.
[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!
http://thedailywtf.com/articles/sponsor-announcement-atalasoft
|
Метки: Announcements |
As Time Goes By… |
In my formative years, I had experienced such things as Star Trek, and the advent of new games like Pong, Space Invaders and Asteroids. As I ventured out of college and into the Orwellian future of 1984, I began a 3+ decade long sojourn into the world of technology. I mused about the wondrous changes that these new-fangled gadgets would bring to all of our lives. Telescreens that connected us both visually and orally in real time. Big Brother. History could be rewritten. Technology would boldly take us where no one had gone before...
Hollerith cards were replaced with Teletypes, then CRTs and finally flat panel displays. You can still fold, spindle and mutilate a flat panel display; it just takes more effort.
Pneumatic tubes were replaced with email and finally text messages. Try as you might, there's simply no way to text someone a live critter.
Interactive Voice Response systems. Talking to a helpful customer service representative is no longer necessary now that we can listen to a recording droning on. After all, don't you just love doing a depth-first search through 17 sub-menus to get what you want?
ARPANET d/evolved into the internet. Google has eliminated the need to have bookshelves of manuals, or remember anything you've ever posted - because it's all there in perpetuity. Granted, a lot of it is filled with pron, but you don't actually have to look at it!
Programming languages. We went from assembly to FORTRAN to C to C++ to Java/.NET/... to scripting languages. While it's true that auto-GC'd languages make it easier to concentrate on what the program must do instead of interfacing with the machine, VB/PHP/Excel/etc. brought programming within reach of those who should not have it. COBOL lives on (as it turns out, the Enterprise does have a mainframe)
Communication. Snail-mail was slow. Email sped things along, but we got impatient so they invented texting. Apple leap frogged a great idea, but only for the truly nimble-fingered.
They still haven't gotten dictation-transcription to work properly; we're nowhere near the point of saying: Computer, build me a subroutine to... because the replicator would spit out a submarine.
Security: Challenge-response questions aren't a bad idea, but too often all the allowed questions can have multiple answers, which forces you to write the Q/A down and keep them nearby (I don't have an older cousin, neither of my parents has a middle name, my first pet was twins and the place I met my wife was in a village in a township in a district in a county).
Security: Requirements that vary wildly for the password-make-up, and change-frequency from system to system and company to company (requisite link). Hmm, 4-8/6-12 characters? Numbers/upper/lower case? Subsets of: ~!@#$%^&*()_+-={}[]:;"'<>,.?/) Change it every 4/6/8/12 weeks? Maybe I'll just go with the fail safe PostIt. FWIW: I haven't had to change the password on my bank ATM account in 35 years because I. Don't. Tell. It. To. Anyone.
Now that the government has shown that any device, no matter how secure, can be cracked, we must all realize that encryption, no matter how sophisticated, ain't cutting it...
Security: We could just write everything in Perl; it would be completely secure after 24 hours (even without encryption) as nobody (including the author) would be able to decipher it (missed opportunity).
Editors: edlin, notepad, vi: when they were all you had, they were a blessing. Notepad++, vim, IDEs, etc: big improvements. But with convenience comes dependency. I once had to edit a config file for a co-worker because they couldn't figure out how to edit it on a *nix production system where Emacs wasn't installed!
Smart phones allow you to concentrate on that all-important email/text/call instead of driving. You can play games (like Pokemon-GO) while behind the wheel, so you can crash into a police car.
Of course, how many times have you texted someone about something only to end up sending an auto-corrected variant (Sweetheart, I'm going to duck you tonight).
Smart cars allow your navigation system to blue screen at highway speeds. This happened to my CR-V, and the dealer told me to disconnect the main battery for 30 seconds in order to reboot the car.
The computer can also modify your input on the gas pedal to make the car more efficient. This sounds like a good thing. Unless you stomp the accelerator through the floor (clearly demanding all the power the engine can give) and the computer decides otherwise, which leads to some very WTF looks from the truck driver that almost pancaked you.
Smart appliances: we no longer need to pester our spouses because, while at the supermarket, we can now contact our appliances directly to see if we need this or that. This will inevitably lead to weekly security-updates for our cars and appliances (you know the day is coming when your fridge and coffee maker start to automatically download and install a Windows-10 update).
Games: from Conway's Life to Sim*, Tetris to Angry Birds, the assorted 80's video and arcade games, Wolfenstein/Doom/Quake/etc., and everything that followed. Games have drastically improved over time and provide tremendous entertainment value. They have yet to build a computer that can count the number of hours of sleep lost to these games.
Miniaturization: they spent zillions creating monstrously large flat panel TVs and then zillions more to get us to watch movies on our phones. After they spent zillions making stuff smaller, they flooded those smaller devices with ads for stuff to enlarge things.
These topics were chosen randomly while thinking back on my career and wandering around my house, and of course, there are many more, but rather than having made drastic improvements in our lives, the changes seem oddly even...
On the other hand, I don't recall Scotty ever having to download a Windows update, and Lt. Uhura never got a robo-call from someone in the Federation (Enterprise: if you would like a scan of the 3rd planet of the system in sector 4, press 3), so maybe the future will be brighter after all.
[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 |
Error'd: Something Seems to be Wrong with the Internet |
"Just perfect. This is not a good day for the entire Internet to break. Thanks for the heads up, Moodle," writes Duro M.
"You know, Amazon, I don't really think I'll be saving much by buying a two pack," writes Rohit D.
Eric wrote, "With your help, you can make their '150k dream' come true."
Pascal wrote, "I was so worried about how much time it would take ot install these 50 place cards, but Amazon has me covered."
"Oh man. I hear you there Yahoo Sports! Every football season, I too get nostalgic for those classic commercials," writes David K.
"VPS from another universe?" Adam K. wrote, "Who makes that happen? A Quantum Sysadmin?"
Peter S. writes, "Carfax has an interesting definition of 'unlimited'."
http://thedailywtf.com/articles/something-seems-to-be-wrong-with-the-internet
|
Метки: Error'd |
CodeSOD: It Takes One Function |
This anonymous submission is the result of our submitter decompiling a Flash application to see how it worked. It’s not often that one thinks to himself, “Wow, some case statements would’ve been a lot better,” but here we are.
private function onMessage(param1:MessageEvent) : void
{
e = param1;
var decoded:Object = JSON.parse(e.data.body);
if(!(decoded.tags && decoded.tags[0] == "trnupdate"))
{
f_bwt("[XMPPConnectionManager] - Message received:\n",JSON.stringify(e.data.body));
}
if(decoded.tags)
{
if(decoded.tags[1] == "ok")
{
var _loc3_:* = decoded.tags[0];
if("getcashgames" !== _loc3_)
{
if("lobby_subscribe" !== _loc3_)
{
if("getcgtickets" !== _loc3_)
{
if("gettourtickets" !== _loc3_)
{
if("getflopsseen" !== _loc3_)
{
if("getbonuses" !== _loc3_)
{
if("getplayerstatus" !== _loc3_)
{
if("getdeeplink" !== _loc3_)
{
if("getbuyins" !== _loc3_)
{
if("getmoneyinplay" !== _loc3_)
{
if("createidentity" !== _loc3_)
{
if("setidentity" !== _loc3_)
{
if("setachbadge" !== _loc3_)
{
if("gethands" !== _loc3_)
{
if("gethanddetails" !== _loc3_)
{
if("gettournames" !== _loc3_)
{
if("getssoticket" !== _loc3_)
{
if("getregistrations" !== _loc3_)
{
if("addPlayer" !== _loc3_)
{
if("removePlayer" !== _loc3_)
{
if("checkFilter" !== _loc3_)
{
if("leave" !== _loc3_)
{
if("getnotiscount" !== _loc3_)
{
if("getnotis" !== _loc3_)
{
if("acknotis" !== _loc3_)
{
if("delnotis" !== _loc3_)
{
if("play" !== _loc3_)
{
if("getflopsneeded" !== _loc3_)
{
if("getRail" === _loc3_)
{
dispatchEventWith("xmppConnectionManager.getRail",false,decoded.payLoad);
}
}
else
{
f_bwt(decoded.payLoad);
}
}
else
{
dispatchEventWith("xmppConnectionManager.slotPlaySuccess",false,decoded.payLoad);
}
}
else
{
dispatchEventWith("xmppConnectionManager.deleteNotification",false,decoded.payLoad);
}
}
else
{
dispatchEventWith("xmppConnectionManager.archiveNotification",false,decoded.payLoad);
}
}
else
{
dispatchEventWith("xmppConnectionManager.getNotifications",false,decoded.payLoad);
}
}
else
{
dispatchEventWith("xmppConnectionManager.getNotificationCount",false,decoded.payLoad);
}
}
else
{
i_gbi.instance.s_xks(decoded);
}
}
else
{
dispatchEventWith("xmppConnectionManager.tournamentValidityCheckSuccess",false,decoded.payLoad);
}
}
else
{
dispatchEventWith("xmppConnectionManager.tournamentUnregistrationSuccess",false,decoded.payLoad);
GameAndTournamentData.instance.y_gvy(decoded.payLoad.trid).unregister();
dispatchEventWith("xmppConnectionManager.tournamentUpdateReceived",false,decoded.payLoad);
x_shy();
}
}
else
{
dispatchEventWith("xmppConnectionManager.tournamentRegistrationSuccess",false,decoded.payLoad);
GameAndTournamentData.instance.y_gvy(decoded.payLoad.trid).register();
dispatchEventWith("xmppConnectionManager.tournamentUpdateReceived",false,decoded.payLoad);
x_shy();
}
}
else
{
d_vin.instance.tournamentRegistrations = decoded.payLoad;
dispatchEventWith("xmppConnectionManager.tournamentRegistrationsReceived",false,decoded.payLoad);
if(!_subscribedToTournamentFeeds)
{
t_ecg("tournament",2);
t_ecg("sitngo",1);
_subscribedToTournamentFeeds = true;
var i:int = 0;
while(i < _subscribedSpecificTournaments.length)
{
sendMessage({
"action":"details_subscribe",
"params":{"ids":[_subscribedSpecificTournaments[i]]}
},_JIDs["target_jid_tournament_monitor"],_monConnectionAvail);
i = Number(i) + 1;
}
}
x_dyr.write("getregistrations-ok");
}
}
else
{
dispatchEventWith("xmppConnectionManager.ssoTicketReceived",false,decoded.payLoad);
}
}
else
{
dispatchEventWith("xmppConnectionManager.tournamentNamesReceived",false,decoded.payLoad);
}
}
else
{
dispatchEventWith("xmppConnectionManager.handDetailsReceived",false,decoded.payLoad);
}
}
else
{
dispatchEventWith("xmppConnectionManager.handsReceived",false,decoded.payLoad);
}
}
else
{
d_vin.instance.j_kuf = decoded.payLoad.achbadge;
dispatchEventWith("xmppConnectionManager.achievementBadgeUpdated",false,decoded.payLoad);
}
}
else
{
d_vin.instance.l_ksj(decoded.payLoad.alias);
dispatchEventWith("xmppConnectionManager.setIdentitySuccess",false,decoded.payLoad);
}
}
else
{
dispatchEventWith("xmppConnectionManager.createIdentitySuccess",false,decoded.payLoad);
q_scp();
}
}
else if(decoded.payLoad.moneyinplay != null)
{
d_vin.instance.w_gwk = decoded.payLoad.moneyinplay;
dispatchEventWith("xmppConnectionManager.balanceChanged");
}
}
else
{
dispatchEventWith("xmppConnectionManager.buyInsReceived",false,decoded.payLoad);
if(_monConnectionAvail && !_monConnection.active && !Constants.y_xxs)
{
_monConnection.connect();
}
else
{
if(Constants.y_xxs)
{
h_dya();
_monConnection.connect();
}
u_isa();
}
}
}
else
{
dispatchEventWith("xmppConnectionManager.deepLinkReceived",false,decoded.payLoad);
}
}
else
{
d_vin.instance.t_ets(decoded.payLoad);
dispatchEventWith("xmppConnectionManager.playerStatusReceived",false,decoded.payLoad);
}
}
else
{
d_vin.instance.g_lbb = decoded.payLoad;
dispatchEventWith("xmppConnectionManager.bonusesReceived",false,decoded.payLoad);
}
}
else
{
d_vin.instance.o_iik = decoded.payLoad.flopsseen;
dispatchEventWith("xmppConnectionManager.flopsSeenReceived",false,decoded.payLoad);
}
}
else
{
d_vin.instance.m_xtn = decoded.payLoad.tourtickets;
GameAndTournamentData.instance.updateTournamentTickets();
dispatchEventWith("xmppConnectionManager.tournamentTicketsReceived",false,decoded.payLoad);
}
}
else
{
d_vin.instance.r_qmn = decoded.payLoad.cgtickets;
dispatchEventWith("xmppConnectionManager.cashTicketsReceived",false,decoded.payLoad);
}
}
else if(decoded.payLoad != null)
{
dispatchEventWith("xmppConnectionManager.tournamentSubscriptionSuccess",false);
x_dyr.write("lobby_subscribe-ok, " + decoded.payLoad.type);
}
else
{
dispatchEventWith("xmppConnectionManager.cashgameSubscriptionSuccess",false);
}
}
else
{
GameAndTournamentData.instance.setCashGames(decoded.payLoad.cashgames);
dispatchEventWith("xmppConnectionManager.cashGamesReceived",false,decoded.payLoad.cashgames);
m_rqq();
x_dyr.write("Cash game data received");
}
}
else if(decoded.tags[0] == "trnupdate")
{
checkUpdate = function():void
{
if(getTimer() - _lastUpdate >= 1500)
{
_showTournamentDataLoader = false;
dispatchEventWith("xmppConnectionManager.tournamentUpdateReceived",false,decoded.payLoad);
dispatchEventWith("xmppConnectionManager.tournamentListUpdated");
}
};
i_gbi.instance.s_xks(decoded);
_lastUpdate = getTimer();
if(_firstTournamentUpdate)
{
_firstTournamentUpdate = false;
Starling.juggler.delayCall(function():void
{
dispatchEventWith("xmppConnectionManager.tournamentUpdateReceived",false,decoded.payLoad);
},2);
}
else
{
Starling.juggler.delayCall(checkUpdate,1.7);
}
}
else if(decoded.tags[0] == "sngupdate")
{
i_gbi.instance.s_xks(decoded);
dispatchEventWith("xmppConnectionManager.sngUpdateReceived",false,decoded.payLoad);
if(!_allowSNGRejoin)
{
_savedSNGJoinMessage = {};
}
}
else if(decoded.tags[0] == "cgupdate")
{
GameAndTournamentData.instance.updateCashGames(decoded.payLoad);
}
else if(decoded.tags[0] == "mscounter" || decoded.tags[0] == "msfactors")
{
e_dql.instance.handleMessage(decoded);
if(_tableConnection)
{
_tableConnection.l_fpt(decoded);
}
}
else if(decoded.tags[1] == "error")
{
try
{
x_dyr.write(!!("error: [" + decoded.tags[0] + "][" + decoded.tags[1] + "]" + decoded.payLoad)?" - code: " + decoded.payLoad.errorcode + "; message: " + decoded.payLoad.message:" (no payLoad)");
}
catch(err:Error)
{
f_bwt("[XMPPConnectionManager] - Decoded.tags[1] error: " + err.message);
}
var _loc5_:* = decoded.tags[0];
if("createidentity" !== _loc5_)
{
if("addPlayer" !== _loc5_)
{
if("removePlayer" !== _loc5_)
{
if("gethanddetails" !== _loc5_)
{
if("getchallenges" !== _loc5_)
{
if("discardchallenge" !== _loc5_)
{
if("challengehistory" !== _loc5_)
{
if("play" !== _loc5_)
{
if("checkFilter" !== _loc5_)
{
if("joinSNGQueues" !== _loc5_)
{
if("gettournames" === _loc5_)
{
dispatchEventWith("xmppConnectionManager.tournamentNamesReceived",false,decoded.payLoad);
}
}
else if(decoded.payLoad.errorcode == "INSUFFICIENT_FUNDS")
{
t_bya(decoded.payLoad.instance);
}
else if(_allowSNGRejoin)
{
t_eqj = function():void
{
if(_savedSNGJoinMessage && _savedSNGTargetJID)
{
sendMessage(_savedSNGJoinMessage,_savedSNGTargetJID);
}
};
Starling.juggler.delayCall(t_eqj,1);
}
}
else if(decoded.payLoad.errorcode == "WRONG_PASSWORD")
{
dispatchEventWith("xmppConnectionManager.tournamentPasswordWrong",false,decoded.payLoad);
}
else
{
dispatchEventWith("xmppConnectionManager.tournamentValidityCheckError",false,decoded.payLoad);
}
}
else
{
dispatchEventWith("xmppConnectionManager.slotPlayError",false,decoded.payLoad);
}
}
else
{
dispatchEventWith("xmppConnectionManager.challengehistoryReceived");
}
}
else
{
dispatchEventWith("xmppConnectionManager.discardchallengeReceived");
}
}
else
{
dispatchEventWith("xmppConnectionManager.challengesReceived");
}
}
else
{
dispatchEventWith("xmppConnectionManager.handDetailsReceived",false,decoded.payLoad);
}
}
else
{
dispatchEventWith("xmppConnectionManager.tournamentRegistrationError",false,decoded.payLoad);
}
}
else
{
dispatchEventWith("xmppConnectionManager.tournamentRegistrationError",false,decoded.payLoad);
}
}
else
{
dispatchEventWith("xmppConnectionManager.createIdentityFailed",false,decoded.payLoad);
}
if("errorcode" in decoded.payLoad && decoded.payLoad.errorcode == "REGISTRATION_CLOSED")
{
if(decoded.payLoad.trid)
{
GameAndTournamentData.instance.y_gvy(decoded.payLoad.trid).registrationEnded = true;
}
else if(decoded.payLoad.sngid)
{
GameAndTournamentData.instance.y_gvy(decoded.payLoad.sngid).registrationEnded = true;
}
dispatchEventWith("xmppConnectionManager.tournamentUpdateReceived",false,decoded.payLoad);
}
}
if(decoded && "payLoad" in decoded && "errorcode" in decoded.payLoad && decoded.payLoad.errorcode == "PLAYER_DISABLED")
{
x_okk();
}
if(decoded.tags[0] == "hello")
{
b_hds(e.data.from.bareJID,decoded.payLoad.instance + "-table");
i_gbi.instance.s_xks(decoded);
}
if(decoded.tags[0] == "hello" && decoded.tags[1] == "init" && false)
{
if(decoded.payLoad.refid)
{
if(decoded.payLoad.sngid)
{
z_ftr(decoded.payLoad.refid);
}
if(Root.e_bdt && Root.e_bdt.tournamentData)
{
Root.e_bdt.tournamentData.sngTournamentId = decoded.payLoad.refid;
}
}
else if(Root.e_bdt && Root.e_bdt.tournamentData)
{
Root.e_bdt.tournamentData.sngTournamentId = -1;
}
}
}
if(decoded.payLoad && !(decoded.payLoad is Number))
{
if(decoded.payLoad.instance || decoded.payLoad.p && decoded.payLoad.p[0])
{
if(decoded.tags[0] == "notify" && decoded.tags[1] == "sngqueue")
{
b_hds(e.data.from.bareJID,decoded.payLoad.instance + "-table");
i_gbi.instance.s_xks(decoded);
}
var instance:String = !!decoded.payLoad.instance?decoded.payLoad.instance:decoded.payLoad.p[0];
decoded.from = e.data.from.bareJID;
if(_tableConnection)
{
_tableConnection.p_ymi(instance,decoded);
}
z_pjv.record(instance,e.data.body);
x_xdz.instance.update(decoded);
if(decoded.tags[0] == "pturn" && decoded.payLoad.p[1] == decoded.payLoad.d[0])
{
dispatchEventWith("xmppConnectionManager.gameUserTurn",false,{"instanceId":decoded.payLoad.p[0]});
}
else if(decoded.tags[0] == "act" && decoded.payLoad.p[1] == decoded.payLoad.d[0])
{
dispatchEventWith("xmppConnectionManager.gameUserAct",false,{"instanceId":decoded.payLoad.p[0]});
}
else if(decoded.tags[0] == "select" && decoded.tags[1] == "buyin")
{
dispatchEventWith("xmppConnectionManager.gameUserTurn",false,{"instanceId":decoded.payLoad.instance});
}
else if(decoded.tags[0] == "buyin" && decoded.tags[1] == "ok")
{
dispatchEventWith("xmppConnectionManager.gameUserAct",false,{"instanceId":decoded.payLoad.instance});
}
}
else if(decoded.tags[0] == "notify")
{
if(_tableConnection)
{
_tableConnection.l_fpt(decoded);
}
i_qdz.instance.s_xks(decoded.payLoad);
if(decoded.tags[1] == "balance")
{
if(decoded.payLoad.balance is Number)
{
d_vin.instance.balance = decoded.payLoad.balance;
}
else
{
d_vin.instance.balance = decoded.payLoad.balance.eur;
var eur:Number = decoded.payLoad.balance.eur;
var ntv:Number = decoded.payLoad.balance.native;
var rate:Number = int(ntv / eur * 100000) / 100000;
d_vin.instance.exchangeRate = rate;
m_vuc.instance.g_qwf("currencyExchangeRate",rate);
}
dispatchEventWith("xmppConnectionManager.balanceChanged",false,decoded.payLoad);
}
else if(decoded.tags[1] == "ach")
{
if(d_vin.instance.l_asy)
{
d_vin.instance.e_lbz(decoded.payLoad.props.achid,"achieved");
EventProxy.instance.dispatchEventWith("updateAchievements");
}
else
{
p_krj();
}
}
if(decoded.tags[1] != "balance" && decoded.tags[1] != "sitouttime" && decoded.tags[1] != "tour")
{
dispatchEventWith("xmppConnectionManager.notificationReceived",false,decoded);
}
if(decoded.payLoad.msg)
{
if(decoded.payLoad.msg[0] == 9 || decoded.payLoad.msg[0] == 11 || decoded.payLoad.msg[0] == 15)
{
j_dja();
}
else if(decoded.payLoad.msg[0] == 16 || decoded.payLoad.msg[0] == 19)
{
u_whk();
}
else if(decoded.payLoad.msg[0] == 17 || decoded.payLoad.msg[0] == 27)
{
x_shy();
}
if(decoded.payLoad.msg[0] == 5)
{
q_scp();
}
}
}
else if(decoded.tags[0] == "getmissions")
{
o_maz.instance.l_kym(decoded.payLoad);
_tableConnection.l_fpt(decoded);
}
else if(decoded.tags[0] == "besthandspromo")
{
p_kmb.instance.n_mbf(decoded.payLoad);
_tableConnection.l_fpt(decoded);
}
else if(decoded.tags[0] == "getachievements")
{
d_vin.instance.l_asy = decoded.payLoad.latest;
dispatchEventWith("xmppConnectionManager.achievementsReceived");
}
else if(decoded.tags[0] == "getchallenges")
{
dispatchEventWith("xmppConnectionManager.challengesReceived",false,decoded.payLoad);
}
else if(decoded.tags[0] == "discardchallenge")
{
dispatchEventWith("xmppConnectionManager.discardchallengeReceived",false,decoded.payLoad);
}
else if(decoded.tags[0] == "challengehistory")
{
dispatchEventWith("xmppConnectionManager.challengehistoryReceived",false,decoded.payLoad);
}
else if(decoded.tags[0] == "getspins")
{
q_xph.instance.f_nac = decoded.payLoad;
}
else if(decoded.tags[0] == "slotupdate")
{
q_xph.instance.k_rho = decoded.payLoad;
}
else if(decoded.tags[0] == "getflopsneeded")
{
q_xph.instance.n_smu = decoded.payLoad;
}
}
if(decoded.tags[0] == "trnupdate")
{
if(_tableConnection)
{
if(decoded.payLoad.tournamentType == "SNG")
{
f_bwt("[XMPPConnectionManager] - SNG trnupdate message received:");
f_bwt(JSON.stringify(decoded));
_tableConnection.l_roq(decoded.payLoad.trid,decoded);
}
else
{
_tableConnection.v_ljh(decoded.payLoad.trid,"MTT");
}
}
}
else if(decoded.tags[0] == "sngupdate" && decoded.tags[1] == "rules")
{
if(_tableConnection)
{
_tableConnection.v_ljh(decoded.payLoad.sngid,"SNG");
}
}
if(decoded.tags[1] == "removed")
{
f_bwt("[XMPPConnectionManager] - Tournament " + decoded.payLoad.trid + " was removed.");
f_bwt(e.data.body);
}
}
[Advertisement]
Otter, ProGet, BuildMaster – robust, powerful, scalable, and reliable additions to your existing DevOps toolchain.
|
Метки: CodeSOD |
Learning to Share |
Maintenance programming is an important job in nearly any software shop. As developers move on to other positions or companies, the projects they leave behind still need someone to take care of them, to fix bugs or implement customer requests. Often, these products have been cobbled together by a variety of programmers who no longer work for the company, many of whom had only a loose understanding of the product (or even programming in general).
Martin was one such maintenance programmer. After being hired, management quickly realized he had a knack for digging into old systems and figuring them out well enough to update them, which often meant a full rewrite to make everything consistent and sane.
One such system that quickly fell into his lap was essentially a management appliance, a Linux virtual machine (VM) prepackaged with a web-based management interface to control the system. The web application worked well enough but the test suite had&trouble.
The tests used Selenium to deploy a fresh VM and perform some pre-defined actions on the user interface. Most of the test suite was written by a former employee named Jackson who, as far as Martin could tell from his notes and source control commit messages, had very odd assumptions about how things worked, especially involving concurrency.
The test suite had some serious performance issues, as well as a ton of inexplicably failing test cases. The system did not scale up as more VMs were deployed, at all, and Martin uncovered the scary truth that Jackson had wrapped everything in synchronization primitives and routed all actions through a global singleton which stored all state for all VMs. Only one test operation at a time was supported, across all test VMs, forcing them to queue up and run sequentially.
Seeing how all test state was stored in a global singleton, Martin realized that a huge number of the test suites failures had to do with leaky state. One test VM would set some state, then give up its lock between tests, providing a small window for another VM to grab the lock and then fail because the state wasnt valid for that specific test.
He asked around the office to see if anyone knew more about the test system, and though nobody knew the specifics, his coworkers did recall that Jackson was hugely concerned that state would leak between test VMs and cause problems and had spent most of his time designing the system to avoid that. So Martin started reviewing source control history and commit messages, and found that Jackson was ignorant of anything beyond basic programming. Somehow, he believed the singleton would prevent state from being shared. Commit messages spelled it out: Used a singleton to avoid shared state for concurrency.
And so Martin spent a few months improving the system by removing the singleton and mutexes, and generally cleaning up the tests code. During testing, Jacksons shared state woes never surfaced, and when Martin was finished the test suite scaled very well by the number of VMs. Most of the spurious test failures simply disappeared and the entire suite ran in a fraction of the time.
And now Martin understood why Jackson was no longer with the company. His solution for dealing with concurrency problems from potential shared state was to rewrite the framework to use assuredly shared state.
[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.
|
Метки: Feature Articles |
CodeSOD: unstd::toupper |
C++ is a language with a… checkered past. Its grown, matured and changed over the decades, and modern C++ looks very little like the C++ of yesteryear. Standard libraries have grown and improved- these days, std feels nearly as big and complicated as parts of Javas class library.
One useful function is std::toupper. Given a char, it will turn that char into an upper-case version, in a locale-aware fashion. What if you want to turn an entire string to upper-case?
You might be tempted to use a function like std::transform, which is C++s version of map. It alters the string in-place, turning it into an upper-cased version. With a single line of code, you could easily convert strings to upper-case.
Or, you could do what Tomeks highly-paid consultant did.
std::string toupper(std::string val)
{
std::string out;
if (val.empty())
return "";
std::for_each(val.begin(), val.end(), std::toupper);
return out;
}
Like a true highly-paid consultant, the developer knew that programmer time is more expensive than memory or CPU time, so instead of wasting keystrokes passing the input as a const-reference, they passed by value. Sure, that means every time this function is called, the string must be copied in its entirety, but think of the developer productivity gains!
Its always important to be a defensive programmer, so in true consultant fashion, well check to ensure that the input string isnt already empty. Of course, since we manipulate the string with std::for_each, we dont actually need that check, its better to be explicit.
Speaking of for_each, it has one advantage over transform- it wont modify the string in place. In fact, it wont modify the string at all, at least as written here. Everyone knows immutable objects cut down on many common bugs, so this is an excellent design choice.
And finally, they return out, the string variable declared at the top of the function, and never initialized. This, of course, is because while your requirements said you needed to turn strings into fully upper-case versions, you dont actually need that. This is a better solution thats more suited to your business needs.
[Advertisement]
Otter, ProGet, BuildMaster – robust, powerful, scalable, and reliable additions to your existing DevOps toolchain.
|
Метки: CodeSOD |
Red Black Trees |
In a good organization, people measure twice and cut once. For example, an idea is born: let's create a data center that is set up properly. First, you figure out how much equipment is needed, how much space is required and how much power is needed to run and cool it. Next, you size back-up batteries and fuel-powered generators to provide uninterruptible power. And so forth.
In a good organization, each of these tasks is designed, reviewed, built, tested and verified, and approved. These things need to be right. Not sort-of right, but right!
Close only counts in horseshoes, hand grenades and thermonuclear war.
Here's a tale of an organization doing almost everything right... almost.
In the late noughties, Mel was working at something that wasn't yet called DevOps at a German ISP. It was a pretty good place to work, in a spanking new office near the French border, paid for by several million customers, a couple of blocks from one of the region's largest data centers that housed said customers' mail and web sites. The data center had all kinds of fancy security features and of course a state-of-the-art UPS. 15 minutes worth of batteries in the basement and a couple of fat diesels to take it from there, with enough fuel to stay on-line, in the true spirit of the Internet, even during a small-time nuclear war. Everything was properly maintained and drills were frequently held to ensure stuff would actually work in case they were attacked or lightning hit.
The computing center only had a few offices for the hardware guys and the core admin team. But as you don't want administrator's root shells to be disconnected (while they were in the middle of something) due to a power outage either, they had connected the office building to the same UPS. And so as not to reduce the backup run time unnecessarily, there were differently-colored outlets: red for the PCs, monitors and network hardware, and gray for coffee makers, printers and other temporarily dispensable devices that wouldn't need a UPS.
Now Germany happens to be known as one of the countries with the best electric grid in the world. Its "Customer Average Interruption Duration Index" is on the order of 15 minutes a year and in some places years can pass without so much as a second of blackout. So the drills were the only thing that had happened since they moved into the office, and not being part of the data center, they weren't even involved in testing. The drills were frequent and pervasive; all computer power cut over to batteries, then generators, and it was verified at the switch that all was well. Of course, during the tests, land-line power was still present in the building on the non-UPS-protected circuits, so nothing actually ever shut off in the offices, which was kind of the whole point of the tests.
When it inevitably hit the fan in the form of an exploding transformer in a major substation, and plunged a quarter million people into darkness, the data center kept going just fine. The admins would probably have noticed a Nagios alert about discharging batteries first, then generators spinning up and so forth. The colleagues in their building hardly noticed as they had ongoing power.
However, on Mels' floor, the coffee maker was happily gurgling along in the silence that had suddenly fallen when all the PCs and monitors went dark.
It turned out that their floor had been wired with the UPS grid on the gray outlets from the beginning and nobody had ever bothered to test it.
|
Метки: Feature Articles |
Error'd: Profound Sadness |
"Shortly after one of our dear colleagues left the business for pastures new, we started to find some messages they left behind," Samantha wrote.
"Apart from not knowing whether I was affected, when, and what to do next, this civil defence warning really put my mind at ease," writes Nick K.
Erwan R. wrote, "I just removed my item, of course it won't be there!"
"I guess seeing an actually valid certificate must be rare enough to trigger a red flag," writes Zach R.
"The child product store Windeln has got you covered," wrote Valts S., "Whether you want to buy a lot of children-related stuff, or avoid it."
Duston writes, "Gosh, who knew that "Error" was a problem? Good thing they gave me the details!"
"This CAPTCHA provider is really testing our stance on gender inclusiveness," wrote James H.
|
Метки: Error'd |
Representative Line: Pointless Revenge |
We write a lot about unhealthy workplaces. We, and many of our readers, have worked in such places. We know what it means to lose our gruntle (becoming disgruntled). Some of us, have even been tempted to do something vengeful or petty to get back at the hostile environment.

But none of us actually have done it (I hope ?). Its self defeating, it doesnt actually make anything better, and even if the place were working isnt, we are professionals. While its a satisfying fantasy, the reality wouldnt be good for anyone. We know better than that.
Well, most of us know better than that. Harris Ms company went through a round of layoffs while flirting with bankruptcy. It was a bad time to be at the company, no one knew if theyd have a job the next day. Management constantly issued new edicts, before just as quickly recanting them, in a panicked case of somebody-do-something-itis. Bob wasnt too happy with the situation. He worked on a reporting system that displayed financial data. So he hid this line in one of the main include files:
#define double float
//Kind Regards, Bob
This created some subtle bugs. It was released, and it was months before anyone noticed that the reports werent exactly reconciling with the real data. Bob was long gone, by that point, and Harris had to clean up the mess. For a company struggling to survive, it didnt help or improve anything. But Im sure Bob felt better.
[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!
|
Метки: Representative Line |
A Painful Migration |
In most companies, business growth leads to greater organizational complexity. With more clients to juggle, owners must increasingly delegate less important tasks to a growing pool of employees and lower management. With time, the org charts grow from simple diagrams to poster-sized trees. Departments and SBUs become separate entities. What was once a three-man startup morphs into the enterprise behemoth we all know and love.
For Vandelay Books, however, this was not the case. Despite becoming one of the largest book distributors in the state, the owners—a husband and wife pair of successful enterpreneurs—kept a firm grip on every single aspect of business. While it helped to alleviate many of the problems found in large enterprises, it also meant several departments were severely understaffed and barely managed. The internal software department, in particular, consisted of a single developer and an occasional intern or contractor ever since the company had started operating.
While it looked like a recipe for disaster, Vandelay Books had two redeeming features: they were hiring, and paying handsomely. For desperate George, who'd nearly exhausted his unemployment emergency fund, all it took was to shake hands with the couple and sign the contract. From there, it was on to a brighter future, assisting with the migration of the company's software suite from an ancient and unsupported database to something more modern.
After setting up his desk and workstation, the owners led George to a grey-haired, scruffy man sitting at the other end of the room.
"This is Doug, our lead developer," the husband introduced.
"Pleasure to meet you." Doug stood and shook George's hand, smiling from ear to ear. "Have you settled in already?"
"I think so, yes," George said. "All I need is a copy of the database to work with."
"I'll get it for you as soon as possible." Doug turned towards his PC and started typing.
After exchanging a few more words with the owners, George left for his desk, expecting the database copy to be waiting in his inbox.
An hour later, George had grown impatient. What's taking him so long? he wondered. It shouldn't take more than a few minutes to run a build script.
He decided to remind Doug about the copy. Doug was at his desk, furiously whacking at the keyboard and grinning to himself.
"Hi, how's that database coming along?" George asked, trying to hide his irritation.
"Almost done!" Doug took his hands off the keyboard, his lips still curved in a beaming smile. "Sorry to keep you waiting, there's a lot of tables in here."
"What do you mean, lots of ...?" George began, but a quick glance over Doug's shoulder answered his question. Instead of a shell window or a database IDE, Doug's display consisted of nothing but a large Notepad window, with the cursor placed in the middle of an unfinished CREATE TABLE statement.
No wonder it takes so long when you're typing the entire database out! George barely held back from screaming at his coworker. Instead, he stepped away as casually as possible and opened his IDE, morbidly anticipating the horrors lurking in the codebase.
A quick skim through the code made George realize why Doug was always smiling. It was the blissful smile of complete and utter ignorance, the smile of someone who'd risen far beyond their level of incompetence and was now eternally grateful for every day things didn't fall apart.
And the code looked like it could fall apart any minute. Over 300,000 lines had been thrown together without rhyme or reason. Obviously, Doug hadn't heard of such modern concepts as "layers" or "structured code," instead opting to hack things together as he went along. Windows API calls, business code, inline strings and pieces of SQL—everything was shoved together wherever it would stick, creating the programming equivalent of a Katamari.
George sat there, pondering all the wrong decisions in his life that'd led to this Big Ball of Mud, until Doug appeared beside him and shook him out of his stupor.
"Oh, I see you're already looking at the code!" Doug said. "It's not that hard to understand, really. I even have a few flowcharts that could help you out! Anyway, you just need to go through each of these commands, one by one—remember, it's not really SQL—like here, when it says SELECT with no FROM like this? It's actually a DELETE. And so on. Simple, isn't it?"
His head spinning, George decided to risk it. "Actually, I was thinking we could structure it a little. Separate those calls out, make a few functions that read or insert records—"
"I beg your pardon?" Doug's smile faded, giving way to the surprised look of a deer in headlights.
"I mean ... uh, never mind."
Sure, the migration would take a hundred times longer Doug's way—but as long as the paychecks cleared, it wasn't worth it to George to fix the unfixable.
Days passed slowly at Vandelay Books, and George's heroic efforts slowly paid off. The code was still terrible despite numerous attempts to improve it when Doug wasn't looking, and the migration wasn't even close to being completed, but George could finally pay his bills and refill his accounts. Once in a while, the owners would stop by for a friendly chat. Between that and the relaxed atmosphere, George began to enjoy the company, if not the job he was tasked with.
Eventually, during one of the conversations with the owners, George felt confident enough to mention that there was a way to get the migration done faster and more efficiently. He hoped they'd be able to convince Doug to let him have more freedom with refactoring the code, or at least fixing some of the most offensive spots.
Instead, all he got were puzzled looks and a long, uncomfortable silence.
The next day, the husband approached him as soon as he entered the office.
"George." The owner's voice was dry and stern. "We've discussed what you said yesterday with Doug, and we've decided we won't be needing your services anymore. Please clear out your desk by today."
George didn't bother arguing. He quietly packed his things, said his goodbyes, and headed back home to polish his resume again. And although he soon found a job at a much more sanely managed company, he often wondered if Doug were still migrating the application one query at a time—and whether he was still able to smile.
|
Метки: Feature Articles |
Coded Smorgasbord: What You Don't See |
Many times, when we get a submission of bad code, were left wondering, what else is going on here? Sometimes, you think, if only I knew more, this would make sense. More often, you think, if I knew more, Id be depressed, because the code is so clearly bad.
For example, Devan inherited a report, built using SQL Servers Reporting Services Report Builder tool. Now, this tool is not intended as much as a developer tool, as a if you can use Excel, you can make a report! It uses Excel-style functions for everything, which means if you want to have branching conditional logic, you need to use the IIF function.
Just dont use it like this.
IIf(Location_Id='01111','Location 1',
......
IIf(Location_Id='99999','Location Help Me','This was the Easy one')))))))))) )))))))))) )))))))))) )))))))))) )))))))))) )))))))))) )))))))))) )))))))))) )))))))))) )))))))))) )))))))))) )))))))))) )))))))))) )))))))))) )))))))))) )))))))))) )))))))))) )))))))))) )))))))))) )))))))))) ))))))))
Now, theres a lot missing here, but we can infer what was skipped, simply by counting the brackets. Even LISP would be embarrassed by those parentheses.
In a different example, Phillip E. was reviewing the days check-ins, when he noticed this particular bit of VB.NET code:
Public Class UserSession
Inherits UserSession(Of UserSession)
...
End Class
Honestly, Im not 100% sure if this is a problem with the code, or the fact that the language lets you abuse names so badly. Then again, the Of UserSession is a type parameter, so I think we can still blame the developer who wrote this.
Angela A wants us to know that misleadingly named constants to replace magic numbers will always be a thing.
const float ZERO = 0.01f;
Taking all of these examples together, Jack S provides a perfect summary of how I feel about this code, this fragment coming from Classic ASP.
function die(s)
[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!
|
Метки: Coded Smorgasbord |
Classic WTF: The Big Ball of Yarn |
It's Labor Day in the US, so we're taking the day off to grill something before the weather turns horrid. While I was finding legacy articles to support the Your Code Might Be Unmaintainable… article, I noticed this classic, and knew that I wanted to re-run it again soon. - Remy
Not too long ago, I posted The Enterprise Dependency. Essentially, it was a visual depiction of a good ole' enterprise framework that was "several dozen megabytes chock full of helper classes like IEnterpriseAuthenticationProviderFactoryManagementFactory." Inspired by the diagram, commenter "LieutenantFrost" shared his own "enterprise-ness and despair" with a dependency diagram that looks somewhat like an anglerfish.
But that got me thinking: like a Representative Line, perhaps dependency diagrams can help provide some insight into the pain that large applications' maintainers face each day. And just then, Jan-Hendrik sent in such a diagram. Note that each little box represents a class, and a line is its dependency to another class.

Jan added, "as people were introducing me to the code, they repeatedly said that parts of the system were so complex that no one dared touch it. My code dependency analyzer (X-Ray) generated this this beautiful spidery ball of fun."
If you have a dependency diagram of your own that would be a good fit for Enterprise Dependency, please send it to me!
[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/classic-wtf-the-big-ball-of-yarn
|
Метки: Feature Articles |
Error'd: What's Wrong with Lorem Ipsum? |
"The date is in the past and there is no time zone specification," writes Hugo K. "But apart from that, everything in this invitation is clear as...verterem mediocritatem?
"Um, yeah. Way to go on that string formatting, Microsoft," writes Austin.
"Sure - the desktop is wide open on this in-store kisok," Hugh S. wrote, "But, I don't even want to think about what happened to make the sign necessary."
"According to the Avis app, I can save a TON if I just wait and pay later," Jeff T. writes.
Maelish wrote, "The Expanse apparently is Normal 0 false false false EN-US X-NONE X-NONE MicrosoftInternetExplorer4."
"Wow! I got to hurry before I miss out on this incredible offer by Living Social!" Simon wrote.
Bartosz writes, "Apparently my slides on slideshare were so awful, some people from Spain and the US decided to un-see them."
http://thedailywtf.com/articles/what-s-wrong-with-lorem-ipsum
|
Метки: Error'd |
Fully Wireless |
Port-au-Prince, I wanna catch a glimpse ...
Summer's winding down, and we're inexorably approaching the fall. Did you take a vacation this year, reader? Maybe you went to the beach; or maybe you live on the beach and you went to the mountains instead, as I did as a girl. Wherever you may have gone, odds are it was a tourist destination. Do you ever wonder what they get up to the rest of the year?
Alexi grew up in just such a tourist town, a little oceanic getaway that was a sleepy ghost town for most of the year, propped up by the brief yet intense tourist season. Her last year of high school, she tried desperately to find a co-op position, but her options were bleak and limited. Finally, she landed the only job she could: the IT guru for the local community college.
At first, it wasn't so bad. She spent much of her time reading books in the office, waiting for the hapless student with wifi troubles or the clueless teacher who needed a password reset. The highlight of her career was the day her senior tech, Aaron, turned off Spanning Tree protocol on a router and then flipped it all back on at once, causing a small fire in the broom closet they were using as a server room.
But for the most part, her days were quiet. Almost ... too quiet.
Jack was a department head, one who was well known by the IT staff. When he stopped by, Aaron immediately ducked into the broom closet. Alexi frowned briefly, but by the time she turned around, she was ready with a polite expression and her asshole-handling kill-'em-with-kindness tone. Her time in retail had prepared her for such a customer.
"Can I help you, sir?"
"Yes, I'm having some trouble with this newfangled Internet classroom," Jack said. "Can you help me?"
No cursing, sputtering, or calling her useless? Then why was Aaron hiding?
"Of course," Alexi replied. "What seems to be the trouble?"
"Well, I set up the microphone and camera just like your instructions said, but my students say they can barely hear me. I even turned up the volume, and nothing."
Now suspecting this to be the oddest prank ever, Alexi launched into the standard bevy of questions. "What kind of microphone are you using?"
"I have a broadcast microphone from the telecommunications department, plugged into about 300 feet of cable."
That was weird, but not unheard of; there was plenty of old equipment floating around. "All right, and where does the cable plug into?" she asked, expecting an audio interface of some type to be involved.
"What do you mean?" Jack asked, a polite yet puzzled expression adorning his face.
"Well, you said there was 300 feet of cable, right? It has to be plugged into something."
"I thought it was wireless."
Wireless? With that long of a cord? "No. That's why it has 300 feet of cable ..."
Sensing that they were both confusing each other more than before, Alexi closed her laptop and asked the gentleman to show her the setup.
The classroom Jack was using was a large lecture hall, complete with a chalkboard and podium. There was a desktop machine sitting under the podium, but Jack had elected to use his own laptop on top of the podium instead, with the camera clipped to the hinge. He demonstrated for her that he was speaking loudly enough as he dragged the microphone, entirely untethered, around the stage area.
A 300-foot tail of black cable dutifully followed him to and fro, but surely was not capable of conveying sound to his laptop. The crappy built-in microphone on the laptop itself, however, was more successful in picking up his voice.
|
Метки: Feature Articles |