Go Forth, Young Programmer |
The past is another planet, but a familiar one. Back in the far off year of 1989, Rick Poleshuck took a job with a company that made a computer product for nurses's stations in hospitals. Now, this product was for notes, and it was an "all inclusive" product- software, proprietary hardware, networking, terminals, everything. And it was written in Forth.
Now, this is what we might call a "classic Forth" system, and in such a system, Forth ran on bare metal. No OS, no filesystem, and a simple scheduler. This was also the system they developed on: the source just lived in raw 1KB blocks on the hard disk, and they edited blocks directly. That's not a WTF, that's just how things were done in that environment.
While this software didn't have any safety implications, it was used in the medical field, so "strict adherence to coding standards" was required. Unfortunately, the coding standard was written by someone with strong opinions, but not a great deal of common sense.
For example, the standard distinguished between code blocks and comment blocks. Each 1KB code block also had a 1KB comment block associated with it. Documentation about what the code block did went in the comment block. No comments went in the code block. In fact, nothing but code went in the code block- and that included indentation. Each code block was essentially supposed to be one extremely long line of Forth code.
The standards made sure developers avoided wasteful and dangerous habits, like documenting their code, but it was silent about some other potentially bad habits. In Forth, for example, nested loops can jump up the hierarchy of loops- the innermost can just jump straight back to the outermost. There are good language reasons for why that's allowed in Forth, but it's the sort of thing that can make your code much harder to understand and harder to debug. So of course, the network stack Rick had to work with used a lot of that convention.
Speaking of unusual conventions, anyone who's done some low-level C programming or looked at OS or driver code, may have seen cases where Assembly gets mixed in with C. Rick's employer took that a step farther- they mixed machine code in with their Forth. "For example," Rick writes, "0x90 is a NOP instruction on the 80286 CPU that was used." It's important to remember that they weren't allowed- by coding standards, not technology- to use any in-line comments in their code blocks, which meant these blobs of machine code were just long strings of hex numbers, with no explanation of what they were doing.
All of these conventions made everyone's lives harder, made the code harder to reason about, but were enforced with an iron fist by the various team leads and management. As different as the real-world constraints of programming Forth in 1989 were, it seems there are certain constants which always follow us: bad policies, enforced for the sake of enforcing them.
Rick has one last thing to add about their process, this time on what they did for source control:
And finally source control was a single master printed copy of the code and a saved 1/2 height 5 1/4" hard disk for each major release.
|
Метки: Feature Articles |
Version Chaos |
Today's submitter, Erica, writes: Every time I tell this story to other developers they don't believe it, because this is quite possibly the dumbest way version control has ever been done.
When Erica joined Initech's mobile team, she came in as a mid-level developer on a team with one senior (Steve) and two interns (Dan and Trevor). Steve wasn't dedicated to the project; in fact, he was 80% on a more important team, so he barely had time for leading the mobile team. It fell to the project manager (Benjamin) to be the technical lead on the project as well as managing the interns. To be fair, this project was mostly meant as a training exercise, something low priority and safe for interns to cut their teeth on. So far, so good.
The app was for Android, and they were using the Flutter SDK, which was at the time on version 0.something and used by almost nobody in the industry—Benjamin's idea. The build was buggy and returned such unhelpful errors that Benjamin couldn't build it on his machine, which was standing in for a CI/CD machine to produce the integrated dev build they were meant to hand off to QA for testing. Rather than taking the chance to teach interns best practices on how to write good code, Benjamin banned code reviews, seeming to believe that any code that ran was good code. Erica's objections fell on deaf ears. She was a girl, what did she know about code? Any time she had a point, such as "Let's not run an entire ecommerce database on a single table", she had to get Dan to say it for her for Benjamin to even halfway consider it.
But none of that is the point of today's story. The true horror of the project lay not in the code quality, but in the version control practices—and I mean both "version control" and "practices" in the loosest sense of the words. Each developer had a branch to work on. Other than Erica, nobody seemed to ever merge master back into their branch. Instead, Benjamin had them each make a pull request at the end of the day into master, and that was the only direction code flowed. So, as you might expect, the branches varied widely within a week or two of this practice.
First thing in the morning, Benjamin would call a team meeting and merge the open pull requests. How would he know which line to take? He'd just ask the developer which line, and take that entire line. As you can imagine, master quickly became an unreadable mess, which is why Benjamin couldn't build from it. Half the time, it wouldn't compile. Erica would merge master into her branch, then spend 2 hours trying to fix everything so it worked, then push her version upstream so Dan could work from her branch. Trevor didn't even bother with her branch, focusing on his own instead, meaning code that worked on his machine bore no resemblance to the code on Erica's. He didn't know any better; he was listening to Benjamin, who was meant to be in charge of the interns.
Sometimes, however, perseverance wins the day. Erica outlived Benjamin on the project, as he finally got moved to something bigger and client-focused. That left her in charge of the interns, with plenty of time to teach them that everything they had learned thus far was wrong.
|
Метки: Feature Articles |
Tales from the Interview: The Whiteboard Challenge |
Like many of us, Igor F keeps his LinkedIn profile vaguely up to date with his career, and that means he inevitably gets messages from recruiters trying to find talent for the latest trendy startup, or the big stable company struggling to find developers happy to work in its code mines, or the contracting company trying to staff up.
Igor usually ignored the pitch, but one recruiter said they were representing a startup in a domain that Igor was really interested in, and was hinting that they had money to burn.
The few message exchanges turned into a phone call. "So, your background is exactly what my client is looking for," the recruiter explained. "Like, literally, your resume is basically the job posting, to a 'T'. I've worked with these folks for a while, and I'm getting the sense that you would get along great with them."
"I mean," Igor said, "I'd like to, but it's probably too early to say that."
"Sure, sure," the recruiter said. "But that's why I want to get you on the phone with them. Their senior principal engineer is ready to have a conversation whenever you are."
Igor nodded along, used to hearing pitches like this. "Well," he said, "I'm not precisely looking for a new job, I've got room to grow in this position-"
"Not like they can give you at Initech. And speaking of growth, let me tell you what their salary range is."
The recruiter said what the salary range was. It was significantly higher than what Igor was making now. Not quite double, but close enough to make the difference academic. With that kind of money flying around, Igor couldn't say no to an interview.
A few days later, Igor, the recruiter, and Dudley, the "senior principal engineer" got together for a call. They went through the usual interview song and dance, and eventually Dudley got to the inevitable programming questions.
"Could you," he said, with a long pause to make it sound like he was thinking up the question on the spot, "oh, I don't know, write me code that'll traverse a tree."
"Yes," Igor said. He waited to hear if they were going to pull up a screen sharing session, or he was going to do a take-home assignment, or what was actually about to happen.
He waited longer.
The silence stretched out to the point where it went from merely uncomfortable to agonizing.
"So," Dudley said, "go ahead."
"Like, now?"
"Yes, just tell me the code that you'd write."
"You mean, just over the phone?"
"Yes," Dudley said, as if that should be perfectly obvious. "Think of it light a whiteboard challenge."
"But… I don't have a whiteboard. And even if I did, you couldn't see it."
"Think of me as your whiteboard," Dudley said. He didn't say, "you idiot," but his tone implied it.
"Okay, so you'd have a function that takes a node as a parameter," Igor started explaining the pseudocode logic that he'd use.
Dudley interrupted. "How do you define that function? This is a coding challenge."
Igor looked at his phone, wondering if he should just hang up, or give it the old college try, or just truck on with the pseudocode explanation. "Uh… let's say for this it could be public, static, traversetree, with node as a parameter."
Dudley huffed. "Don't you mean 'parenthesis, Node n, close parenthesis, open curly bracket? This is a coding challenge."
Igor did his best to verbally type out the Java syntax for a depth-first tree traversal. Each time he didn't specify a bracket, or a semi-colon, or a parenthesis, Dudley was ready to point out his mistake. Even pulling up a text editor so that he could type along didn't exactly make it easy to symbol-by-symbol "type" code over the phone.
The interview ended the way all awkward interviews do, with some mumbled "thanks for your time," and a hasty end to the call. Igor never heard back about the job, or from the recruiter. But it was definitely clear that he hadn't gotten "along great" with anyone on that call.
|
Метки: Tales from the Interview |
Error'd: Something or Nothing at All |
"I didn't know that I could buy an empty shopping cart from name.com, but here I am," Tom writes.
Calvin K. writes, "Samsung is really confused here...Samsng is really confused here....
"I think I'll get a big raise if I can get the DO NOT ISSUE certification." wrote Thomas J.
David B. wrote, "After my payment info, they seem to think I still owe them, just not very much."
"Just did an induction questionnaire for a venue. Usually these things are annual, looks like I hit the jackpot on this one!" Justin R. wrote.
https://thedailywtf.com/articles/something-or-nothing-at-all
|
Метки: Error'd |
CodeSOD: A Match Made In… |
Andy C writes:
One of our colleagues dug this code up from an outsourced project. Took a few us to try to find out what it actually does, we're still not completely sure.
This is the associated Java code:
if (productList != null && !productList.isEmpty()) {
for (int i = 0; i < productList.size(); i++) {
String currentProductID = String.valueOf(productList.get(i).getProductId());
String toMatchProductID = String.valueOf(currentProductID);
if (currentProductID.equals(toMatchProductID)) {
productName = productList.get(i).getProductName();
break;
}
}
}
If you just skim over the code, something you might do if you were just going through a large codebase, it looks like a reasonable "search" method. Find the object with the matching ID. But that's only on a skim. If you actually read the code, well…
First, we start with a check- make sure we actually have a productList. The null check is reasonable (I'll assume this predates Java's Optional type), but the isEmpty check is arguably superfluous, since we enter a for-loop based on size()- an empty list would just bypass the for loop. Still, that's all harmless.
In the loop, we grab the current item (item 0, on the first iteration), and that's our currentProductID. We choose to cast it into a string, which may or may not be a reasonable choice, depending on how we represent IDs. Since this is imitating a search method, we also need a toMatchProductID… which we make by cloning the currentProductID.
If the currentProductID equals the toMatchProductID, which it definitely will, we'll fetch the product name and then exit the loop.
So, what this method actually does is pretty simple: it gets the productName of the first item in the productList, if there are any items in that productList. The real question is: how did this happen? Was this a case of copy/paste coding gone wrong? Purposeful obfuscation by the outsourcing team? Just a complete misunderstanding of the requirements corrected through quick hacking without actually fixing the code? Some combination of all three?
We know what the code does. What the people writing it do, that we're definitely not sure about.
|
Метки: CodeSOD |
CodeSOD: Callback Bondage |
"Garbage collected languages can't have memory leaks," is well established as a myth, but we still have plenty of code which refuses to clean up after itself properly.
An anonymous submitter was working with a single-page-app front-end which wraps a stream abstraction around a websocket. Messages arrive on the stream, and callbacks get invoked. When certain parameters change, new callbacks need to be registered to handle the new behavior. The old callbacks need to be unbound- and it's that step this code doesn't do.
const channelName = this.channel.name;
this.channel.bind('updated', (data) => {
if (this.channel.name === channelName) {
this.updateData(data)
}
});
The bind method attaches a new callback to a given channel. Without a matching unbind to remove the old callback, the old callback will sit in memory, and keep getting invoked even as it does nothing useful. Over time, this leads to performance issues.
Or, at least, it could lead to performance issues. The original developer had a… special solution to handling garbage collection. I'll let our submitter explain:
On the plus side, their cleanup logic for the component that uses this data unsubscribes all open websocket channels across the entire app on unmount, including channels owned by unrelated components, so we can rest assured that eventually they'll definitely be gone. Along with everything else.
|
Метки: CodeSOD |
CodeSOD: Put in Order |
Rust is one of the "cool" languages these days. It promises all the low-level power of C with memory safety and "modern" programming conventions like iterables and maps. High performance, expressive language, low-level power seems like a great combination for certain domains.
Now, Jenna Winchester needed to do some Morton Coding or Z-indexing, which is an algorithm which lets you take multidimensional points and turn them into 1-dimensional points in a way that maintains their spatial relationships- essentially a fast way of traversing a quadtree. It's a fairly simple and fast algorithm, especially if you implement it using bitwise operations. A naive implementation, without optimizations, can do its job with very few CPU cycles, relatively speaking.
And while Jenna could have implemented her own version of it, never reinvent a wheel that someone else probably has. So she tracked down a Rust library (or crate, if we're using Rust terminology) which promised to do the job. Jenna's expectation was that she could feed in her 5-dimensional point, and get back the z-index by simply doing something like let output = input.z_index(). Let's call the library morty_code, because we should focus more on the painful experience of working with a badly designed API than worry about calling out a library for a mildly niche language for a very specific problem domain.
That, of course, would be too easy. The code which Jenna needed to write to perform the core purpose of what the library claimed to do was this:
fn morton_encode_u8_5d_zdex (input: [u8; 5]) -> u64 {
use zorder::*;
let usize_bits = 8*core::mem::size_of::<usize>();
let transmute_input = |x: &u8| -> FromU8 {(*x).into()};
input // Take the original input,
.iter() // element by element...
.map(transmute_input) // Transform each to the custom input types zindex needs...
.z_index() // Compute the result...
.unwrap() // Panic if there's an error... (Can there be one? Who knows!)
.iter_storage() // Take the result usize by usize...
.fold(0 as u64, |acc, a| (acc<as u64)
// ...and finally, unify the iterator of usizes into a single u64.
// Can you just FEEL the ergonomics?
}
Now, even if you don't know Rust, which I don't, this looks menacing, even before you read Jenna's comments. Here's the key things: you can compute the Z-index using bitwise operations. The library author, however, didn't understand this, or didn't care, so instead used a different datastructure: a vector of bits. The line where we define transmute_input invokes FromU8, which takes an 8-bit number and turns it into an 8-item vector of bits. Which, despite knowing that it will always need exactly 8 items to hold 8 bits, the actual implementation of FromU8 dynamically allocates that memory.
So, with that in mind, we can trace through the implementation. We take our 5-dimensions of 8-bit integers as input. We iterate across each one, converting each to a vector-of-bits using .map(transmute_input), for each dimension, we can then calculate the z_index(), which comes back as a vector-of-bits, so we have to unwrap() it. We chunk the results back up using that iter_storage() and then finally we can reduce the z-indexes for each dimension using fold to bitshift them around.
If that seems like a lot of work to implement a simple algorithm first described in the 1960s, you'd be right. Jenna ran some performance tests comparing her naive implementation with the implementation from this library:
I checked the assembly that's emitted for a simple case of two u32s to one u64. A very naive version needed 600 machine instructions.
morty_codeneeded more than three thousand. And since it contains multiple subroutines,morty_codeturns out to be two orders of magnitude slower than the naive version.
But hey, we wouldn't want to use the naive version, because we'd have to worry about things like edge cases and faulty assumptions which surely means the library has to be more correct, right?
I whipped up a couple simple tests to ensure that the functions operate correctly. Surprise! The
morty_codeversion doesn't. It ends up putting the high-significance bits at the end and the low-significance bits at the beginning. Printing the vector-of-bits directly shows the result correctly, but printing it after transforming it into a u64 shows the bits reversed.
Which is to say that the internal representation surprises you with its endianess. I suspect that it was that endian problem which initially lead to the creation of the vector-of-bits type that's used internally, but there are far easier ways to resolve conflicts with byte order.
Jenna contacted the original developer of the library, hoping to maybe help improve the experience for other developers.
This was the point at which I decided that the code has absolutely no redeeming features. A few fruitless posts of dialogue later, I realised that talking to TDWTF would be much more productive than talking to the maintainer. So... here we are.
Here we are, but where is "here" on the z-order curve?
|
Метки: CodeSOD |
It's a Gift |
Tyra was standing around the coffee maker with her co-workers when their phones all dinged with an email from management.
Edgar is no longer employed at Initech. If you see him on the property, for any reason, please alert security.
"Well, that's about time," Tyra said.
They had all been expecting an email like that. Edgar had been having serious conflicts with management. The team had been expanding recently, and along with the expansion, new processes and new tooling were coming online. Edgar hated that. He hated having new co-workers who didn't know the codebase as intimately as he did. "My technical knowledge is a gift!" He hated that they were moving to a CI pipeline that had more signoffs and removed his control over development. "My ability to react quickly to needed changes is a gift!" He hated that management- and fellow developers- were requesting more code coverage in their tests. "I write good code the first time, because I've got a gift for programming!"
These conflicts escalated, never quite to screaming, but voices were definitely raised. "You're all getting in the way," was a common refrain from Edgar, whether it was to his new peers or to management or to the janitor who was taking too long to clean the restroom. It seemed like everyone knew Edgar was going to get fired but Edgar.
Six months later, the team was humming along nicely. Pretty much no one thought about Edgar, except maybe to regale newbies with a tale of the co-worker from hell. One day, Tyra finished a requirement, ensured all the tests were green in their CI server, and then submitted a pull request. One of her peers reviewed the request, merged it, and pushed it along to their CD pipeline.
Fortunately for them, part of the CD step was to run the tests again; one of the tests failed. The failing test was not anything related to any of the changes in Tyra's PR. In fact, the previous commit passed the unit test fine, and the two versions were exactly the same in source control.
Tyra and her peers dug in, trying to see what might have changed in the CD environment, or what was maybe wrong about the test. Before long, they were digging through the CD pipeline scripts themselves. They hadn't been modified in months, but was there maybe a bad assumption somewhere? Something time based?
No. As it turned out, after many, many hours of debugging, there was an "extra" few lines in one of the shell scripts. It would randomly select one of the Python files in the commit, and a small percentage of the time, it would choose a random line in the file, and on that line replace the spaces with tabs. Since whitespace is syntactically significant in Python that created output which failed with an IndentationError.
A quick blame confirmed that Edgar had left them that little gift. As for how it had gone unnoticed for so long? Well, for starters, he had left during that awkward transition period when they were migrating new tools. The standard code-review/peer-review processes weren't fully settled, so he was able to sneak in the commit. The probability that it would tamper with a file was very low, and it wouldn't repeat itself on the next build.
It was hard to say how many times this had caused a problem. If a developer saw the unit test fail after accepting the PR, they may have just triggered a fresh build manually. But, more menacing, they didn't have 100% unit test coverage, and there were some older Python files (mostly written by Edgar) which had no unit tests at all. How many times might they have pushed broken files to production, only to have mysterious failures?
In the end, Edgar's last "gift" to the team was the need to audit their entire CI/CD pipeline to see if he left any more little "surprises".
|
Метки: Feature Articles |
Error'd: Nope, that was Prod |
"They say you shouldn't test in prod... They aren't wrong." Dave P. writes.
Dave W. wrote, "I guess even the USPS's 'missing mail' site is missing in action."
"$69.99 instead of $69.99...Thank you, Microsoft?" writes Carlos.
Pascal wrote, "Looks like the test was more successful than they had planned."
"Looks like eBay is keeping it as simple as possible," Peter K. writes.
|
Метки: Error'd |
Going Backwards |
Nearly 15 years ago, fresh out of college, Giuseppe was hired on at a mobile networking company. The company wasn't a software company, but since they were heavy in networking and could handle all sorts of weird technical problems there, software must basically be the same thing, so they also started producing applications to manage those networks.
It didn't take them long to discover that "building networks" and "writing software" are different skillsets, and so they did what every company with some room in the budget does: they hire a pack of Highly Paid Consultants to get their project on track.
Giuseppe joined the team just as the budget for consultants ran out. They "knowledge transfered" "everything" in a whirlwind of incomprehensible PowerPoint presentations, and then vanished back into whatever lairs HPCs come from, and left Giuseppe and the other new hires to figure out what was going on.
When the HPCs were hired, the company had extensively specified the requirements. Each requirement was expressed in terms like:
There was an elaborate tracking system, with multiple signoffs, to confirm who tested what and when. That specific requirement, 12.2.23.a.2, was signed as "passed" by the original HPC. Then it was passed off to a QA test, which also signed off. Then it went to the users, who submitted a defect: the back button often doesn't behave as expected. It drifted back to the original developer, who closed the defect as "can not replicate", QA signed off again, the user re-opened the ticket claiming the issue was still there, and round and round until the budget for consulting ran out and it was Giuseppe's turn to figure out what was actually going on.
There was no automated testing, and no formal test plan, but it didn't take Giuseppe long to figure out what the bug was. In fact, it took slightly more than 6 navigation actions, to discover that he could replicate the bug consistently. It also explained why the HPC couldn't replicate the bug: they navigated through exactly six pages, and confirmed that if they hit the back button 5 times, they got the same pages they'd been through. If they'd gone to one more page, they would have seen that it didn't work. And the offending code was easy to spot:
private void OnPageNavigated(Page pageFrom)
{
if(m_backStack.Count < 5)
{
m_backStack.Push(pageFrom);
}
}
Instead of tracking the last five pages the user viewed, this tracked the first five pages they viewed, and ignored every navigation thereafter.
This was just one of many bugs around the back button alone, and additional confusion about the specs. Many pages might open a modal dialog- should that be in the history? If it is, does it count against the 5 pages total? It ended up creating situations where the stack of navigation events needed to track more than 5 items, because of the fuzzy definition of what was a "page", but that created additional bugs because if you could navigate backwards more than five times, you were technically in violation of the spec.
Giuseppe adds:
As I've moved up the software engineering ladder I cringe about how the project was run and managed. Things like unit testing or CI didnt exist, all testing was extremely manual, performed by a dedicated team of 3 testers to an exhaustive specification. … As a [junior] developer nobody ever performed a code review on what I wrote, it went straight into the app. In the whole time I worked there only one person gave me feedback on my code when I screwed up OTA updates that amounted to 'dont do it again'. I was there for over 2 years, and it should be no surprise that the product still hadn't been signed off when I left.
It wasn't signed off when he left, and it probably hasn't been signed off to this day.
|
Метки: Feature Articles |
CodeSOD: Copy Paste Paste Paste Paste |
"Hey," someone on Russell F's team said. "We should add some keyboard navigation to our web app." That struck everyone as an "easy win", specifically because they were really talking about making "enter" and "escape" do something useful.
They wrote some quick requirements, passed it off, and the developer submitted a surprisingly long pull request. Russell passed a sample of three out of the dozens of methods:
function lr_control(event) {
var current = document.getElementById('lr');
var next = document.getElementById('rf');
var back = document.getElementById('lf');
if (event.key == "Enter") {
divareInoperativeSensorsReplacedShow();
}
if (event.key == "Escape") {
unCheck(current);
}
nav_control_clickable(current, next, back, event);
return;
}
function rf_control(event) {
var current = document.getElementById('rf');
var next = document.getElementById('rr');
var back = document.getElementById('lr');
if (event.key == "Enter") {
divareInoperativeSensorsReplacedShow();
}
if (event.key == "Escape") {
unCheck(current);
}
nav_control_clickable(current, next, back, event);
return;
}
function rr_control(event) {
var current = document.getElementById('rr');
var next = document.getElementById('yesReplace');
var back = document.getElementById('rf');
if (event.key == "Enter") {
divareInoperativeSensorsReplacedShow();
}
if (event.key == "Escape") {
unCheck(current);
}
nav_control_clickable(current, next, back, event);
return;
}
At no point did the similarity between all these methods ever make the developer responsible think, "oh, there's gotta be an easier way." No, they just copied and pasted, again and again, changing only the document elements that they point at.
Russell supplied some comments on the request, and pointers on how to streamline this code.
https://thedailywtf.com/articles/copy-paste-paste-paste-paste
|
Метки: CodeSOD |
The Contract Position |
Mandi didn't plan to take a staff job at a university. To the contrary, she'd heard some bad things: loads of office politics, budgets so thin you need quantum mechanics to describe their position, and bureaucracy thick enough to drown any project.
But one day, she met her old colleague Scot for lunch, and they got to chatting about his university job. "Oh, yeah, that's common enough," he said, "which is why my team isn't structured that way. We're doing in-house development of educational solutions, which is a fancy way of saying 'nobody understands what we do, so they leave us alone'."
Scot invited her to take a tour of his office, meet some of his co-workers, talk a little about the work they were doing. They were based in a rented office just at the edge of campus, sharing the floor with a few scrappy startups. It wasn't a fancy space, and it was a little cramped, but the first and last thing Mandi noticed was how happy everyone was to be there.
Augusta, the front-end lead, talked a little about their framework selection process, and how they made their choices, not based on what was new and trendy, but based on what felt like a really good fit for their subject matter. Harry, who handled the middleware, was happy to explain how he'd needed some time to get up to speed on the right cloud scaling options, but the team was there to support him, and they eventually got a great set of automation built which handled spikes but kept costs down. Quinn rhapsodized about how great it was to work closely with the end users, to really build the solution that worked best for them, and how exciting it was to see their requirements translate into implemented software with tangible benefits.
Unlike pretty much any place Mandi had ever seen, everyone was happy to be there. Everyone liked the work they were doing. Everyone felt empowered to make the best choices, to work through challenges with the rest of the team, and everyone enjoyed celebrating their successes together.
"I always have to bring people in," Scot said, "because nobody believes me when I tell them about how great my job is."
"Honestly, I still don't believe it," Mandi said.
"Well, I did have a bit of an ulterior motive. We're looking to scale up the team a bit, which means I'll have a position soon. It'll take a little bit to grind those gears- that has to go through the university hiring process, but I hope you apply. I think you'd be a great fit."
Mandi did apply, when the position finally opened up. It was a slow-moving interview process, mostly through the university HR department, but she met Scot one more time, early in the process. Then, she landed the job, a contract-to-hire position.
At that point, Scot didn't work there anymore. He had resigned, and since the team was actively working, and since the HR process was painfully slow, the HR department didn't hire a replacement as an employee- they hired a contractor. Technically, Mandi worked under the same contract, and thus her direct manager was Cyril, the new team lead.
There was just one problem with that: by both university policy and IRS rules, contract employees can't manage regular employees. So Cyril's title was just "scrum master", and he technically had no management authority. Which meant the regular employees ignored him.
Mandi and one other contractor reported to Cyril, but nobody else did.
The overall project lead, Ruthie, was also a contractor, but hired through a different contracting firm. Not only did she have no authority over regular employees, she had no authority over any other contractors. Nobody reported to her, but she was in a management role.
The result of this management omni-shambles was meetings. Loads of meetings. Daily standups became daily "take a load off, we'll be here awhiles". After the standup, Cyril would be pulled into meeting after meeting as every section of the department started pulling in different directions, so despite being the "scrum master", he had no idea what anyone on the team was doing. Ruthie threw meetings on everyone's calendar, which nobody attended, because nobody worked for Ruthie. The only way for a contractor to get a regular employee's attention was to schedule a meeting.
Above both Ruthie and Cyril was the technical lead for the entire campus IT department. He was the only person in the org chart that everyone technically reported to, but he had never been a fan of the entire "rent an off campus office and let smart people solve problems," approach. While he was the only one who could potentially set some direction, he had no interest in doing so. The one time Mandi was on a conference call with him, he excused himself, "This isn't really a priority for me right now, I have other issues I need to address that are more important."
Mandi stuck it out until the end of her contract period. She never received an offer for a full-time position, and frankly, she wouldn't have taken it anyway. Her fellow subcontractor, the only other person who reported to Cyril, did. So his HR hiring process can work, eventually, for some people.
|
Метки: Feature Articles |
CodeSOD: Generated Requests |
If you've worked with developing software of any real complexity, you've probably come across a library or tool that does code generation. Instead of writing all the boring boiler-plate stuff yourself, you maybe throw a little configuration at the system and let it generate all those classes for you. I'd argue that, in most cases, that sort of thing is a code smell- it's not in and of itself bad, but it hints as missed abstractions. There's probably a general way to represent what you want to represent without generating a big pile of classes. The question is: is the additional abstraction worth it, or should you just generate code to get it done?
Russell was recently browsing the documentation for Amazon Web Services. The Java SDK allows you to send requests to AWS to automate things in their cloud. When you send a request, you need an AmazonWebServiceRequest object. Now, AmazonWebServiceRequest is itself an abstract class, so we need to send a concrete implementation, specific to the operation we want to perform. That means it's going to be one of:
AbortDocumentVersionUploadRequest, AbortEnvironmentUpdateRequest, AbortMultipartUploadRequest, AbortMultipartUploadRequest,
AbortVaultLockRequest, AbstractPutObjectRequest, AcceptCertificateTransferRequest, AcceptDirectConnectGatewayAssociationProposalRequest,
AcceptDomainTransferFromAnotherAwsAccountRequest, AcceptGrantRequest, AcceptHandshakeRequest, AcceptInboundCrossClusterSearchConnectionRequest,
AcceptInputDeviceTransferRequest, AcceptInvitationRequest,
…
VerifySoftwareTokenRequest, VerifyTrustRequest, VerifyUserAttributeRequest, ViewBillingRequest,
VoteOnProposalRequest, WithdrawByoipCidrRequest, WithdrawByoipCidrRequest, WriteRecordsRequest
I skipped a… few thousand of them in the middle there. You can get them all on the docs page. If you dig into any of the concrete implementations, they're all tagged as @Generated. I'm sure that there are more convenient methods which wrap around this, and that your average SDK user is never going to need to directly interact with any of these classes, but when you see a wall of generated classes, you can't help but wonder if this is the best approach. At a certain level, SDKs are meant to be understood by humans, and if you have about 9,000 auto-generated types, maybe you've lost the plot just a little bit.
And if you're worried about how you handle the response, don't be. Each of these request classes has a Result version as well. Well, not each of them- there's only 8,704 Result types. Now that's efficient.
|
Метки: CodeSOD |
Error'd: Winter ...Delivered! |
"I wanted to find out how much snow Providence got overnight and apparently, Amazon wants to sell me some of it," Francis B. writes.
John C. wrote, "Yes, Windstream, I have both questions and feedback on this."
"Well, I mean, both haggis and nappies are kind of intestine related," writes Andy.
Robert F. writes, "My propane tank is full of 84E84b089... Are you sure that's not my septic tank?"
"In an attempt to connect with more voters, it looks like Trump is growing a placeholder moustache," wrote Hugh S.
|
Метки: Error'd |
Best of…: Best of 2020: Web Server Installation |
While this year has felt endless, there are projects which will feel like they take forever. As we wrap up our tour of the best of 2020, let's visit an endless project. Original -- Remy
Once upon a time, there lived a man named Eric. Eric was a programmer working for the online development team of a company called The Company. The Company produced Media; their headquarters were located on The Continent where Eric happily resided. Life was simple. Straightforward. Uncomplicated. Until one fateful day, The Company decided to outsource their infrastructure to The Service Provider on Another Continent for a series of complicated reasons that ultimately benefited The Budget.
Part of Eric's job was to set up web servers for clients so that they could migrate their websites to The Platform. Previously, Eric would have provisioned the hardware himself. Under the new rules, however, he had to request that The Service Provider do the heavy lifting instead.
On Day 0 of our story, Eric received a server request from Isaac, a representative of The Client. On Day 1, Eric asked for the specifications for said server, which were delivered on Day 2. Day 2 being just before a long weekend, it was Day 6 before the specs were delivered to The Service Provider. The contact at The Service Provider, Thomas, asked if there was a deadline for this migration. Eric replied with the hard cutover date almost two months hence.
This, of course, would prove to be a fatal mistake. The following story is true; only the names have been changed to protect the guilty. (You might want some required listening for this ... )
Day 6
Day 7
Day 8
Day 9
Day 16
Day 19
Day 20
Day 22
Day 23
Day 28
Day 29
Day 30
Day 33
Day 35
Day 36
Day 37
Day 40
Day 41
Day 42
Day 47
Day 48
Day 51
Day 54
Day 55
Day 56
Day 57
Day 58
Day 61
Day 62
Day 63
Day 64
https://thedailywtf.com/articles/best-of-2020-web-server-installation
|
Метки: Best of |
Best of…: Best of 2020: Science Is Science |
You do not need formal training from a compsci program or similar before you're allowed to be a developer. But sometimes, when your job role already contains "engineer" in the title, people think you can handle any engineering task. As we continue our review of the best of 2020, here's a tale of misapplied human resources. Original --Remy
Bruce worked for a small engineering consultant firm providing custom software solutions for companies in the industrial sector. His project for CompanyX involved data consolidation for a new oil well monitoring system. It was a two-phased approach: Phase 1 was to get the raw instrument data into the cloud, and Phase 2 was to aggregate that data into a useful format.
Phase 1 was completed successfully. When it came time to write the business logic for aggregating the data, CompanyX politely informed Bruce's team that their new in-house software team would take over from here.
Bruce and his team smelled trouble. They did everything they could think of to persuade CompanyX not to go it alone when all the expertise rested on their side. However, CompanyX was confident they could handle the job, parting ways with handshakes and smiles.
Although Phase 2 was officially no longer on his plate, Bruce had a suspicion borne from experience that this wasn't the last he'd hear from CompanyX. Sure enough, a month later he received an urgent support request via email from Rick, an electrical engineer.
We're having issues with our aggregated data not making it into the database. Please help!!
Rick Smith
LEAD SOFTWARE ENGINEER
"Lead Software Engineer!" Bruce couldn't help repeating out loud. Sadly, he'd seen this scenario before with other clients. In a bid to save money, their management would find the most sciency people on their payroll and would put them in charge of IT or, worse, programming.
Stifling a cringe, Bruce dug deeper into the email. Rick had written a Python script to read the raw instrument data, aggregate it in memory, and re-insert it into a table he'd added to the database. Said script was loaded with un-parameterized queries, filters on non-indexed fields, and SELECT * FROM queries. The aggregation logic was nothing to write home about, either. It was messy, slow, and a slight breeze could take it out. Bruce fired up the SQL profiler and found a bigger issue: a certain query was failing every time, throwing the error Cannot insert the value NULL into column 'requests', table 'hEvents'; column does not allow nulls. INSERT fails.
Well, that seemed straightforward enough. Bruce replied to Rick's email, asking if he knew about the error.
Rick's reply came quickly, and included someone new on the email chain. Yes, but we couldn't figure it out, so we were hoping you could help us. Aaron is our SQL expert and even he's stumped.
Product support was part of Bruce's job responsibilities. He helpfully pointed out the specific query that was failing and described how to use the SQL profiler to pinpoint future issues.
Unfortunately, CompanyX's crack new in-house software team took this opportunity to unload every single problem they were having on Bruce, most of them just as basic or even more basic than the first. The back-and-forth email chain grew to epic proportions, and had less to do with product support than with programming education. When Bruce's patience finally gave out, he sent Rick and Aaron a link to the W3 schools SQL tutorial page. Then he talked to his manager. Agreeing that things had gotten out of hand, Bruce's manager arranged for a BA to contact CompanyX to offer more formal assistance. A teleconference was scheduled for the next week, which Bruce and his manager would also be attending.
When the day of the meeting came, Bruce and his associates dialed in—but no one from CompanyX did. After some digging, they learned that the majority of CompanyX's software team had been fired or reassigned. Apparently, the CompanyX project manager had been BCC'd on Bruce's entire email chain with Rick and Aaron. Said PM had decided a new new software team was in order. The last Bruce heard, the team was still "getting organized." The fate of Phase 2 remains unknown.
https://thedailywtf.com/articles/best-of-2020-science-is-science
|
Метки: Best of |
Best of…: Best of 2020: The Time-Delay Footgun |
As we revisit the best articles of 2020, have you been wondering why 2020 has been such a… colorful year? Maybe the developer responsible wrote a bad version check. Original --Remy
A few years back, Mike worked at Initech. Initech has two major products: the Initech Creator and the Initech Analyzer. The Creator, as the name implied, let you create things. The Analyzer could take what you made with the Creator and test them.
For business reasons, these were two separate products, and it was common for one customer to have many more Creator licenses than Analyzer licenses, or upgrade them each on a different cadence. But the Analyzer depended on the Creator, so someone might have two wildly different versions of both tools installed.
Initech wasn’t just incrementing the version number and charging for a new seat every year. Both products were under active development, with a steady stream of new features. The Analyzer needed to be smart enough to check what version of Creator was installed, and enable/disable the appropriate set of features. Which meant the Analyzer needed to check the version string.
From a user’s perspective, the version numbers were simple: a new version was released every year, numbered for the year. So the 2009 release was version 9, the 2012 was version 12, and so on. Internally, however, they needed to track finer-grained versions, patch levels, and whether the build was intended as an alpha, beta, or release version. This meant that they looked more like “12.3g31”.
Mike was tasked with prepping Initech Analyzer 2013 for release. Since the company used an unusual version numbering schema, they had also written a suite of custom version parsing functions, in the form: isCreatorVersion9_0OrLater, isCreatorVersion11_0OrLater, etc. He needed to add isCreaterVersion12_0OrLater.
“Hey,” Mike suggested to his boss, “I notice that all of these functions are unique, we could make a general version that uses a regex.”
“No, don’t do that,” his boss said. “You know what they say, ‘I had a problem, so I used regexes, now I have two problems.’ Just copy-paste the version 11 version, and use that. It uses string slicing, which performs way better than regex anyway.”
“Well, I think there are going to be some problems-”
“It’s what we’ve done every year,” his boss said. “Just do it. It’s the version check, don’t put any thought into it.”
“Like, I mean, really problems- the way it-”
His boss cut him off and spoke very slowly. “It is just the version check. It doesn’t need to be complicated. And we know it can’t be wrong, because all the tests are green.”
Mike did not just copy the version 11 check. He also didn’t use regexes, but patterned his logic off the version 11 check, with some minor corrections. But he did leave the version 11 check alone, because he wasn’t given permission to change that block of code, and all of the tests were green.
So how did isCreatorVersion11_0OrLater work? Well, given a version string like 9.0g57 or 10.0a12, or 11.0b9, it would start by checking the second character. If it was a ., clearly we had a single digit version number which must be less than 11. If the second character was a 0, then it must be 10, which clearly is also less than 11, and there couldn't possibly be any numbers larger than 11 which have a "0" as their second character. Any other number must be greater than or equal 11.
Mike describes this as a “time-delayed footgun”. Because it was “right” for about a decade. Unfortunately, Initech Analyzer 2020 might be having some troubles right now…
Mike adds:
Now, I no longer work at Initech, so unfortunately I can’t tell you the fallout of what happened when that foot-gun finally went off this year.
https://thedailywtf.com/articles/best-of-2020-the-time-delay-footgun
|
Метки: Best of |
Best of…: Best of 2020: Copy/Paste Culture |
As per usual, we'll be spending a few days looking back at some of our favorite stories of the year. We start with a visit to a place where copy/pasting isn't just common, it's part of the culture. Original -- Remy
Mark F had just gone to production on the first project at his new job: create a billables reconciliation report that an end-user had requested a few years ago. It was clearly not a high priority, which was exactly why it was the perfect items to assign a new programmer.
"Unfortunately," the end user reported, "it just doesn't seem to be working. It's running fine on test, but when I run it on the live site I'm getting a SELECT permission denied on the object fn_CalculateBusinessDays message. Any idea what that means?"
The problem was fairly obvious, and Mark knew exactly what the error meant. But the solution wasn't so obvious. Why did the GRANT script work fine in test, but not in production? How can he check to see what the GRANTS are in production? Is there someone specific he should ask to get permission to look himself? Does the DBA team use a sort of ticketing system maybe? Is this even the right approach? Who on his team could he even ask?
Fortunately, Mark had the perfect venue to ask these sorts of questions: the weekly one-on-one with his team lead, Jennifer. Although he had a few years of coding experience under his belt, he was brand new to The Enterprise and specifically, how large organizations worked. Jennifer definitely wasn't the most technical person he'd met, but she was super helpful in "getting unblocked" as he was learning to say.
"Huh", Jennifer answered in their meeting, "first off, why do you even need a function to calculate the business days between two dates?"
"This seems like something pretty common in our reports," Mark responded, "and this, if the logic ever changes, we only need to change it in one place."
Jennifer gave a mystified look and smiled, "Changes? I don't think the 7-day week is going to change anytime soon, nor is the fact that Saturday and Sunday are weekends."
"Well, umm," Mark definitely didn't expect that response. He was surprised to have to explain the basic principles of code reuse to his supposed mentor, "you see, this way we don't have to constantly rewrite the logic in all the places, so the code is a bit simpler."
"Why don't you just copy/paste the calculation code in your queries?" she rhetorically asked. "That seems like it'd be a lot simpler to me. And that's what I always do…. But if you really want to get the DBAs involved, your best contact is that dba-share email address. They are super-slow to project tickets, but everyone sees that box and they will quickly triage from there."
Needless to say, he didn't follow Jennifer's programming advice. She was spot on about how to work with the DBA team. That tip alone saved Mark weeks of frustration and escalation, and helped him network with a lot more people inside The Enterprise over the years.
##
Mark's inside connections helped, and he eventually found himself leading a team of his own. That meant a lot more responsibilities, but he found it was pretty gratifying to help others "get unblocked" in The Enterprise.
One day, while enjoying a short vacation on a beach far, far away from the office, Mark got a frantic call from one of his team members. An end-user was panicked about a billables reconciliation report that had been inaccurate for months. The auditors had discovered the discrepancies and needed answers right away.
"So far as I can tell," his mentee said, "this report is using a fn_ CalculateBusinessDays function, which does all sorts of calculations for holidays, but they already prorate those on the report."
The problem was fairly obvious, and Mark knew exactly what happened. Some must have changed the logic on that function to work for their needs. But changing it back would mean breaking someone else's report. And the whole idea of a function seemed strange, because that would mean taking a dependen--
The junior programmer interrupted his stream of thought.
"I think I should just add an argument to the function to not include holidays," he said. "That's really simple to do, and we can just edit our report to use that argument."
"Ehhh," Mark hesitated, "the logic is so simple. Why don't you just copy/paste the business day calculation? That's the simplest solution… that's what I do all the time."
https://thedailywtf.com/articles/best-of-2020-copy-paste-culture
|
Метки: Best of |
CodeSOD: Classic WTF: 2012 |
As we enjoy our holiday today, in this seemingly unending year of 2020, our present to you is a blast from 2012, the year the world was supposed to end. Original --Remy
"Most people spend their New Year's Eve watching the ball drop and celebrating the New Year," writes Jason, "and actually, that's what I planned to do, too. Instead, I found myself debugging our licensing activation system." "Just as I was about to leave the office, I received a torrent of emails with the subject 'License Activation Failed'. One or two every now and then is expected, but dozens and dozens at four o'clock on New Year's Eve... not so good. It took me a moment to realize the significance of 4:00PM, but then it hit me: I'm on Pacific Time, which is UTC -8 hours.
"The error message that was filling up our logs was simply 'INVALID DATE' and for the life of me I couldn't figure out why. Our license code was a 32-bit number that represented the expiration date of the license and the features in the license. 7 of those bits represented the year since 2000, so obviously the date was fine up until 2127. After hours and hours of digging through PL/SQL, Java, JavaScript, Ruby, and some random shell scripts, I found the following.
IF YEAR = 2012 GOTO INVALIDDATE
Jason continued, "nowhere in the code was any indication why 12 would not work, so I took it out. Figuring it couldn't make things any worse, I published the code and tested the registration system. It worked. In the end, a meaningless IF statement had shut down our renewals business... just because."
|
Метки: CodeSOD |
CodeSOD: Classic WTF: Developer Carols |
It's the holiday season, which means over the next few days, we'll be reviewing some of the best of 2020, if anything about 2020 can be considered "the best", and maybe some other surprises. To kick things off, we're going to pull from the faroff year of Christmas 2017, and return to our Developer Carols. That year, we ran them too late to go caroling, and this year, nobody outside of New Zealand should be going caroling, keeping with our tradition of meeting the requirements but delivering absolutely no value. (Original)
It’s Christmas, and thus technically too late to actually go caroling. Like any good project, we’ve delivered close enough to the deadline to claim success, but late enough to actually be useless for this year!
Still, enjoy some holiday carols specifically written for our IT employees. Feel free to annoy your friends and family for the rest of the day.
Joy to the world,
We’ve pushed to prod,
Let all,
record complaints,
“This isn’t what we asked you for,”
“Who signed off on these requirements,”
“Rework it,” PMs sing,
“Rework it,” PMs sing,
“Work over break,” the PMs sing.
Back the system up to tape drives,
Fa la la la la la la la la,
TAR will make the tape archives,
Fa la la la la la la la la,
Recov'ry don't need no testing,
Fa la la la la la la la la la,
Pray it works upon requesting,
Fa la la la la la la la la
Vertical height,
Align to the right,
CSS,
Aid my fight,
Round the corners,
Flattened design,
!important,
Please work this time,
It won't work in IE,
Never in goddamn IE
On my nth day of helpdesk, the ticket sent to me:
12 write arms leaping
11 Trojans dancing
10 bosses griping
9 fans not humming
8 RAIDs not striping
7 WANs a-failing
6 cables fraying
5 broken things
4 calling users
3 missing pens
2 turtled drives
and a toner cartridge that is empty.
(Contributed by Charles Robinson)
Here comes a crash bug,
Here comes a crash bug,
Find th’ culprit withgit blame,
Oh it was my fault,
It’s always my fault,
Patch and push again.Issues raisin‘, users ‘plainin’,
Builds are failin’ tonight,
So hang your head and say your prayers,
For a crash bug comes tonight.
WCry the Malware, was a nasty ugly worm,
With a cryptolock and a bitcoin bribe,
Spread over SMBWCry the Malware, is a Korean hack they say,
But the NSA covered up the vuln,
To use on us one dayThere must have been some magic in that old kill-switch they found,
For when they register’d a domain,
The hack gained no more groundWCry the Malware, was as alive as he could be,
Till Microsoft released a patch,
To fix up SMB
(Suggested by Mark Bowytz)
Oh come, all ye web devs,
Joyful and triumphant,
Oh come ye to witness,
JavaScript's heir:Come behold TypeScript,
It’s just JavaScript,
But we can conceal that,
But we can conceal that,
But we can conceal that,
With our toolchain
Thanks to Jane Bailey for help with scansion. Where it's right, thank her, where it's wrong, blame me.
https://thedailywtf.com/articles/classic-wtf-developer-carols
|
Метки: CodeSOD |