I don't disagree with your main points, but you might be surprised to hear some things that people will say and unnecessary optimizations they'll attempt...
Have you used any Java API? That's exactly the feeling I get from using them.
Basically, the same thing as he's saying.
Almost all similar articles I read, are used as some kind of self justification to writing bad code and leaving it there forever.
If you believe the software will fail (won't ever be changed/grow), do whatever you want with it. If you think it'll succeed, stack too many dirty hacks together and you're gonna pay for it later with tons of interest.
Main problem is, too many times this goes "unnoticed", people just take longer and longer to do the same things, but people stay oblivious to the real causes of that technical debt.
Nerds (of which I am one) tend to be really good at rationalization. These articles serve as a sort of catharsis ("I probably could have done better, but I was really just moving fast and breaking things!"). Other times, these articles are written as a means of projecting identity (e.g. "I reject enterprise-y nonsense by refusing to doing any sort of analysis or design!")
Both are exceedingly poor motives.
This is not a bad formula for crafting a blog post that you want to get picked up via HN. It is also a great way to start a discussion that will never end.
Its a bit like Scientology, but without the crazy.
Oh no, wait .. there's plenty of crazy.
Developers very often use technical debt or doing the right thing or similar arguments as an excuse not to understand an existing codebase and past choices, to convince management or fellow developers to rewrite something that doesn't need to be rewritten, or to buy time to learn and try new technologies/methodologies/patterns that are supposed to be better without understanding tradeoffs or to satiate their desire to overengineer and build a cathedral, when a simple cart would suffice. These efforts acrrue, rather than pay off, actual technical debt. A simple hardcoded one-off is a lot easier to throw away or fix than a half-assed "elegant" design with configuration options and tight coupling everywhere.
All this means developers and managers are correct to be skeptical when another developer talks about technical debt as a reason that they can't do something quickly. It's not that elegance and technical debt and doing the right thing, etc, don't matter.
Just as developers are often skeptical of a business leader's motivation (is it right for customers or just for his/her ego? etc.), business leaders can be skeptical of technical debt and code quality arguments not because of the concepts themselves but the motivations behind the person doing the arguing.
This kind of behavior seems malicious but I think it's more often the result of wishful thinking. A lot of these come as a result of imitating good design without understanding the context that makes those choices good or even necessary.
Anyway it makes a good point that business objectives come before "best practices" which I agree with, but you need to be careful when cutting corners that you don't actually sabotage your business objectives. The real discussion is actually on a risk level: if I cut corner X, I can potentially make revenue Y unless cutting corner X causes a problem and I incur loss Z. Cutting that corner is only good if
P(no problems | cutting corner X) * Y > P (problems | cutting corner X) * Z
If this equation looks tough, it's because it is tough. This is not an easy decision and you really need to be on top of your game to choose correctly. Alternatively, Y must be much bigger than Z and you have some faith in your shortcut. But for most dev work, a lot of the time you're going to be getting that equation wrong if you just guess it and it's why so many experienced devs will tell you not to cut corners.I am currently developing and maintaining a system which powers an ongoing core business process. If it goes down, employees basically sit around unable to accomplish work. I hope that this system will be robust enough to continue operating for years, maybe decades, and anticipate that during that time four or five business units will either transition their processes to this system, or create new processes that use this system. Clean, quality, well-tested and extensible code clearly matter here in a way that they don't for a one-off Christmas 2013 promotion.
I think this captures the central problem in our current software development state of affairs. Because as developers we are stuck with primitive tools that make doing even trivial stuff time consuming, we have been conditioned to view every piece of code we write as a "will be maintained forever" proposition. You don't generally see people using excel and worrying about the maintainability of their spreadsheets, because even sophisticated spreadsheets are trivial to create and so the default mentality is use and throwaway.
This is a problem we are trying to tackle at my startup, a web developer tool that you can use to quickly put out applications to support day-to-day operations without obsessing about whether you're going to need to build a spaceship out of your current requirement.
That said, I think the operative decision is "should I cut the corner?". If so, you should also factor in the p( ___ | not cut corner ) probabilities.
So, you should cut the corner if the benefits exceed the costs. Written out:
p(~P|C) * b(~P) - p(P|C) * c(P)
>
p(~P|~C) * b(~P) - p(P|~C) * c(P)
Symbols: p = probability; b = benefits ($); c = costs ($);
P = problems; ~P = no problems; C = cutting corner; ~C = not cutting corner.Rearranging to:
p(~P|C) * b(~P) - p(~P|~C) * b(~P)
>
p(P|C) * c(P) - p(P|~C) * c(P)
Then you get a simpler form: b(~P) * (p(~P|C) - p(~P|~C))
>
c(P) * (p(P|C) - p(P|~C))
An amazing fortune awaits any person who can simply evaluate this inequality in real-world situations.It is also useful to know when the cost of gathering the information to evaluate the expression is not worth the potential benefit!
Everyone just thumb sucks it in the end anyway - I'll just embed this domain name in the distributed binary, nobody will ever steal our domain name! Technically someone at this point should pull up the statistics for stolen domain names to get an estimate for the probability, and work out how much it would cost to set up a system to pull a correct domain name from a backup, and then work out the cost to redistribute all the binaries to end users whose software won't be working for some time. I doubt anybody has ever done that particular calculation.
I think that's about all that needs to be said.
Yeah, and that's like a perfect definition for "ad hominem" argument.
"[I]n which a claim or argument is rejected on the basis of some irrelevant fact about the author [...]"
Leon is a really nice, super funny guy, but the fact that no one can maintain the code he writes is highly relevant to the subject of the article. In summary, http://lwtc247.files.wordpress.com/2010/06/get-a-brain-moran...
This is not a disaccreditation of the man based upon his character or something unrelated to the topic, his ability to write good code is highly relevant to the issue at hand.
I've followed "hey, it works" developers who knocked out functionality quickly. Their by-product was Lovecraftian code.
I work with the same types of devs, and modifying/extending their code could lead one to alcoholism. Seriously. No joke.
I have quite honestly considered changing teams, just to get away from a code base that may "work", but induces nightmares.
There's no shame in this IMHO, and lots of very good programmers have to add inelegant shims to solve specific edge cases. I enjoyed reading this article (previously linked on HN by another) which I think has some relevance here: http://www.gigamonkeys.com/code-reading/
"Make it work, then make it beautiful, then if you really, really have to, make it fast."
(The quote continues "90 percent of the time, if you make it beautiful, it will already be fast. So really, just make it beautiful!")
In fact, I disagree with this. The reason is that "elegant" usually means "decoupled" whereas many optimizations actually consists in "recoupling" to take advantage of special cases or to share resources among various decoupled objects.
Designing software to be fast and elegant is actually quite challenging problem. Of course, that makes it even more interesting.
Interestingly, too, you indicate "recoupling". That is, to couple again. Meaning for you to even be in this situation, realizing what you need to do, you had to first make it elegant. Otherwise you'd be optimizing...what, exactly? You have no metric.
I think we all understand that spaghetti code simply adds time to the next feature we want to introduce, probably creates unseen bugs, and might explode. But it's always a balance and nothing is black and white.
I hear a lot of "you're wrong, do it the right way first!" but you gotta balance "the right way" with its context. Code isn't made for code's sake, it's made to do something (like a business objective, like "be able to order a book through my browser"). Businesses are trying, first and foremost, to make profit, however they need to do that. Beautiful code has nothing to do with that goal, but is sometimes a means to that end.
If the code achieves the business objective it was meant to achieve, it's doing its job (however minimally). Customers don't care what the code looks like (if they look at it). They just care that it does what they need to do (like order a book). Businesses don't care what the code looks like as long as it makes profit.
Secondary to the business objective is how that code fits into the greater context of the system-as-a-whole. Obviously badly written code is going to accrue technical debt, which will likely lead to cumulative delays to new features, cause things to break, etc. The extra effort involved in dealing with that debt is obviously going to cost something, which will have an affect on the primary goal of business: profit. But also delaying a feature, to "do it right the first time", that could be earning revenue, is also going to affect profit. The key is to balance it to maximize profit overall.
When I do anything (code, draw, write, cook, paint, dance, whatever) I start by doing it the simple, easy, fast way; I probably fuck it up; then I iterate and try to improve upon it and refactor. Usually in getting something out there I discover new solutions that I would not have seen had I not actually made something and put it into the world where I could look at it and point to it and talk about it with other people. Being an early mover aside, I think this kind of process helps overall quality anyway. And in the meantime I've got something that's at least doing something.
I think Joe Armstrong knows what he's talking about ;)
My clients/employers don't have a say in the quality of the finished product they receive. (They may be angry about this, but my responsibility is to my profession first, and them second.)
> If the client needs a Christmas promotion, and you
> deliver the best product in the history of promotions --
> on December 29th -- it's worthless.
Isn't the point to set a deadline that allows the product to be built to a certain standard of quality? If you only ever have time to write untested copy-paste "code that fucking works" then you don't have a problem with your development approach ... unless you're convinced that's the only way to do things.Tony Wilson: "You've got the posters? It's the fucking gig!"
Peter Saville: "Yeah, I know - it just took ages to get the right yellow."
Tony Wilson: "The gig's over."
Peter Saville: "I know."
Tony Wilson: "It looks fucking great actually - yeah, really nice. It's beautiful - but useless. And as William Morris once said: "Nothing useless can be truly beautiful."'
Therefore, no art is truly beautiful?
If I see sloppy code, or have to write sloppy code, I move to another company, and will continue to do so. So how's that employee turnover rate (hint, use the internet archive and check the about us page)?
Over the years, I gradually drifted from doing things the Right Way to doing things that are good enough. Doing things elegantly in practice unfortunately adds zero flexibility for non-trivial changes in business direction: you simply can't foresee everything. One swift decision at mid-management level and your elegant framework suddenly becomes an unwieldy Titanic impossible to put into a quick turn.
Now arguably works-for-today kind of solutions are not inherently any more flexible. However, they don't take nearly as much investment in the first place.
(Should also point out that quick and simple is not equal "sloppy code": I check my return values and handle errors just fine, it's just that my code makes no attempt at establishing world peace).
Yes and no. If you're writing very elegant statically typed functional code like in Haskell or ML, you're going to have to be elegant at the design level, and yes, you might have trouble adapting to new requirements. But if you're in a domain where requirements aren't always set in stone, you're probably using a more dynamic environment where you don't have to be what the OP called "elegant" at the design level (e.g. his unrealistic caching layer example), but you can still write elegant code at the micro level (within functions, classes, etc.), which will almost certainly lead to substantially less technical debt and shouldn't cost much if any more time than quick hacks at the micro level.
Bullshit. Most deadlines in software engineering are fake. There are some that aren't, but they are exceptions, not a rule. If you're writing all your code as if every deadline is the Judgment Day, you're doing software engineering wrong and not making the right trade-offs.
The unfortunate reality is that there are some engineers who will gladly deliver software 5% faster and 600% worse just because it makes them look good with the management. They justify their behavior by all kinds of BS, but in the end it hurts the industry, their clients and the society at large.
(There is another breed of engineers that will deliver software 600% slower and 200% worse because of overcomplicated design. Yeah, that's also a pathology. But it's not like you have to choose only between these two pathologies.)
In my view, the reasons for such artificial deadlines are a) so all involved parties can plan their schedules, and b) because an undefined timeline has a tendency to become an infinite timeline. Rarely do these two needs imply dire consequences for a missed deadline (provided it's not missed by an excessive duration).
This kind of post is always the same, but any decent developer knows when you have to just "get it done" and when you should take time to make it the right way. And of course, that dirty code will be refactored later on.
Always using the "f*ing works" strategy just means you're mediocre at writing code. Either because you led the management team to think anything can magically be built in a "hackathon" or you just don't care about what you build.
In my experience, the same product development lifecycles and business priorities that led to the dirty code in the first place will cause it to live forever. Code that works doesn't tend to get refactored unless it needs to be revisited for some other business purpose.
Personally I make it clear to them that if they need something impossibly fast, I'll need to come back for it later or it will cost us in the long run. When you do that, it's their choice to leave crappy code there, and later on you can point that out, when you need double the time to change that crappy little piece of code.
But I've seen managers (that should know better) that just take the fastest path always and just don't care, unfortunately it happens :(
You're a software developer. Me too. But we may not have the same goals and requirements. In fact there are several different worlds of software development, and different rules apply to different worlds.
Not all software projects are the same, no more than all construction projects are the same. You can quickly nail a dog house together with only vague planning and minimal craftsmanship. But if you want to build a house for people, you have to plan more, pay more attention to what you're doing, and make sure the result meets the local codes. If you want to build a skyscraper, you have to put even more work into planning, robust engineering, and code-meeting than you do with a house.
http://www.amazon.com/Software-Architecture-Primer-John-Reek...
I've worked on software for iPhones and on software for jumbo jets. The two activities almost aren't even the same thing. Drawing on other life experiences, I might liken it to jazz improvisation vs. classical performance, or to mobile phone snapshots vs. professional portraiture. Overlapping, yet different enough that methods which work for one do not necessarily make sense for the other.
Note that much of the attitude comes from a belief that what is good for the company is ultimately good for the engineer.
However, I'll suggest that's not always the case. Take for example one of the OA's reflections:
"Technical debt should be weighted against the actual ROI, because in many cases it is more cost effective to launch early. This way, though you may be accruing technical debt, you are also accruing revenue immediately, and you can reconcile the debt over time."
Unfortunately, dependant upon your company, that last part may never happen and if I were to frequently experience that at a company, well, you better believe I would rather quit and if I couldn't quit I still would rather put my foot down and pump the tech debt argument for all it's worth, otherwise, in the longer term, working there would be a living hell.
I know I'll get a lot of flak for this take, but all I can say is that while I've quit companies like that, I understand why some people couldn't do the same, and at that point it may require some tough love people management in effort to create a brighter future with a healthy work place.
Wish I could upvote you twice.
My company has so much technical debt in the system that we have five people doing the job of three. Technical debt is almost never paid and the result is invariably a big-ass mess.
You might then ask, "well if the company's profitable then what's the effing problem, then?" The answer is that there's a ginormous difference between where they could be and where they are now.
I'm slowly coming to understand that broken is normal. Paying off technical debt can cause your company to mutate superpowers simply because nobody else does it.
What is the cost of it breaking? $0? $1,000? $10,000?
What is the cost to fix the broken code?
What is the cost to maintain it?
How much developer time do you waste down the line because they have to fix the "working" code?
I say these things because I've seen this attitude expressed in a large codebase and it sucks a lot to work with and has wasted many hours of developers' lives.
Technical debt is almost never paid back. Just know that going in to whatever you are building.
That is absolutely spot on. One of the hardest thing is explaining to management that the code your co-worker wrote in a "flash" three years ago is still eating up developer time today. We have a few ill-conceived or badly written systems that seems to eat up time and constantly interfere with new projects. The problems are pretty much invisible to management, because the developers spend a few hours here and there proppring up the badly written systems. Over time you end up having to spend all your time just putting out fires rather than helping the business moving forward.
So, 6 months ago, perhaps the least difficult thing to do to make a new spreadsheet report was to copy and paste the first one and change a few bits of the query. Repeat 10 times. Management is happy, look how many reports we churned out. Maybe you thought about making a generalized reporting system or something, but your boss was pressuring you. "It's so simple, just do the simplest thing. Just make it work."
But now that we have a dozen reports, that asshat in management says he wants graphs on these reports, even though he told us 6 months ago "no way in hell" would he ever want graphs on these reports, now he's telling us we're "such typical programmers, no fucking common sense." Suddenly the change isn't so easy, we have to touch a metric shit tonne of lines of code.
Technically speaking, it's going to be fewer lines of code to change to copy-pasta the graphs into all of the existing reports. But we only need to get burnt once to realize that this management douchebag is going to try to burn us again in the future. So, we figure it's only a small percentage more difficult to completely rewrite the reporting system from scratch. And then it will be easy to add new features in the future.
He is correct in one sense, it should have been done that way in the first place, but that is the nature of getting burnt, you put your trust into someone who was untrustworthy. The management asshole put pressure on you to get the "simple" reports out fast, slow-rolled you on the additional reports, then doesn't want to hear "excuses" or "details" or anything other than "the job is done."
So that's where so-called "over engineering" comes from. It's not. It's self preservation. And at your next job, you'll remember it should have always been that way, and you dig your heels in on every design decision. And suddenly you're "that guy" who is always saying "we tried that at our last job and it didn't work."
I have a policy that I always say I can get the job done. I never tell the client that they can't have what they want. But I never compromise on quality. What you want takes a certain amount of effort to create, make sure it's good, and make sure it doesn't impede our future efforts. Working in this way ensures I establish a consistent expectation for how the work will go and the quality of the work over the lifetime of the project. Consequentially, I get the work done on time and everyone is happy with it, almost always.
This applies to throwaway code vs. maintainable code as well.
In programmer's head there's a running judgement ongoing while crafting something new: there are constant decisions on if cheating with quick'n'dirty hacks is the way of least effort for one part of the software or whether the total effort will be minimised by expending more effort now to do it in a more reusable way that saves effort later.
For example, some parts are just written quickly to test another part more quickly, and that another part might be contained in a module whose interfaces are really worth designing first and coding later. But some of the code inside of that could be replaced by a quick hack for now. Good things and bad things interveawe, interline, and intertwine and in the end just the right places are done perfectly and just the other right places are cobbled together just enough to keep the system together.
We occasionally do misses there, but good programmers have a pretty good hunch on which way to lean at which stage of development and which part of the code.
> If the client needs a Christmas promotion, and you deliver the best product in the history of promotions -- on December 29th -- it's worthless.
There's almost always another option - if it's going to be a complete mess that will give everyone additional work in January, then maybe the scope needs to be cut and the remaining features polished.
Even if the code is completed, it can be worthless (or worse):
- if it crashes your whole site for all users, rather than just the promotion part
- if it exposes security issues
- if it not only fails on edge cases on the day, but wakes up your on-call people and spoils their christmas with work
> And taking it a step further, a veteran programmer should know when and, most importantly, how to cut corners, if needed, to meet the deadline. Which brings me to my next point: over-engineering.
It must be great to work at a company that only has fully aware veteran programmers that never make mistakes when cutting corners. It's a shame there's no recruitment link.
> This way, though you may be accruing technical debt, you are also accruing revenue immediately, and you can reconcile the debt over time.
A couple of paragraphs before he talked about limiting scope actually. Yet, we're back to the only balance most people talk about - time and money (expected revenue from change). I've seen something different everywhere. You talk about technical debt, you plan to get rid of it, then another big idea comes and you add more technical debt to get the shiny feature out. Getting rid of technical debt happens years later when people simply cannot live with the issues - they turned from annoyance that takes you 5 minutes to workaround into company-wide issues that block releases for days.
He's also ignoring the fact that the technical debt is cummulative - every time you do something quickly you add a couple more minutes to the every following time you need to work on the same code. Sure - it may allow to get this feature out quicker, but it will also force you to cut corners on the next feature - because you don't have the abstractions you need available.
I'm tempted to assume that the author never really had to deal with the issues someone else left in the code, because they also thought they're veteran programmers and know where to cut corners.
The last part of that statement is troubling, because it happens so rarely. I think a lot of devs want to lean towards more engineering "up-front" because they've been smacked repeatedly with the business reality that the first version of that code is what you're going to be stuck working with, and building on top of, for a long time.
Of course, those of us with a bit more experience know that a badly designed module can very easily get enmeshed in a system, with the whole system being designed with the bad module's deficiencies being taken into account. Now you don't just have to refactor the bad module, but all of the code around it too, which turns out to be a difficult ask, so nobody does it, and the bad code becomes locked in.
As always, the higher up the software stack you work, the easier it is to justify bad code, but when you are providing infrastructure code, that will be used by applications, you had better be sure that your interfaces are clean, and your implementation stable. Try pushing some inelegant code to the Linux kernel, and you won't enjoy to the experience...
I started working as a Unix sysadmin in 1996, and while I have always written programs, and have been doing so more heavily lately, I never wrote a program with someone else before. All of my programming has either been programs completely written by me, or small patches sent to existing open source projects. The purpose of testing aside from the most rudimentary always eluded me.
A few months ago I wrote my first program along with a good, professional programmer. I would code, he would code, I would code. He was good, but I noticed some of his changes would break my code. It occurred to me that testing is something of a communication device. I create functionality, I implement the test, then I tell other commiters to run the test suite before commiting code. If the tests do not complete, they know their code will break functionality I made.
I guess this is obvious, but when I read top reasons to do TDD etc. it is usually not mentioned as a prominent reason to do testing, if it is mentioned at all. I do not find tests beyond the most simple kind helpful in my code, but it starts becoming more useful when you have other people modifying the code.
Deploying ugly piece of shortcut that is going to give you grief for the next 5 years is not worth making for the sake of pulling your skin off to met unrealistic deadline that was created by the layer of clueless management.
Follow your professional intuition of course.
If your product is a first iteration on a web application, user interaction may be a key part to quality while database tuning may not.
If your product is a data analytics service, database tuning may actually be very important while the user interaction may be less so.
In my opinion, it's better to focus on improving what is measurable. That means focusing on making more things work than making things more elegant.
Of course, this whole debate is a broad generalization and the devil is in the details.
I have been rewiting some of my own code recently.It was a quick temporary solution that grew and grew. I used OOP to avoid cutting and pasting, but I realise that one central method didn't have any concrete set of input or output paramaters, it changed depending on what was calling it - very nasty. It was essentially doing three things, rather than one. (It worked, as the guy in the article descrbes)
I think seperation of concerns, making one function do one thing etc, is a start in making elegant code. Or at least not fugly like it was before.
I also have to question whether this is really good from a business perspective. "First mover advantage" really is overrated. Rather than cutting corners to launch your product a couple of days sooner, perhaps you should be focusing on the long term.
Granted, there are all kinds of caveats we can think of. For example, very early startups don't have the option to focus on the long term and just need to ship something. But even then, putting in a little bit of time up front to polish your code will greatly help your velocity for your next couple of features. You are planning on having more features past the first couple, right?
Nearly everything is revisited and changed at some point over the life of the product. If the initial design is a hack or not well thought out, the change will likewise be uglier than the original code.
Even worse, if the original code is not understood by the next developer, it will be thrown out and rewritten.
Take the extra time in the early stages to design, think about fault tolerance, possible future enhancements, extensibility, robustness, reusability, etc. It will pay off.
Yes I over engineer nearly everything I design and yes my deadlines are met. With an elegant design, it is often easier to make late changes on a project. If you follow understood design patterns other people can jump into a project and pick it up right away.
Plus elegance is often a bullshit notion and when people say it what they really mean is "what I personally like".
It's an article chock full of caveats (Best practices vary wildly across the board, except for the ones any decent programmer obviously knows.), straw-men (Someone spending a week to implement caching on a 20-row table probably wasn't crafting elegant code anyway.), and contrived anecdotal examples (Christmas apps must be out by Christmas. My God, what a breakthrough. Most projects are not nearly this straightforward.).
Actual article text is a bit more nuanced than the linkbait title. I still hate reading sentiments like these because they appeal to the lazy parts of ourselves that don't need any additional encouragement. It's like people revel in their own laziness and failure to follow-through on some things.
Professionals finish the job, regardless of how they feel.
However, it helps tremendously for imperfect code to exist in a disciplined higher-level organization. In particular for OOP, this means interface and package/assembly responsibilities should be clearly-defined and leak-free. To me, this is the minimum ideal--it may never be definitively achieved, but one should aim for no less. Then, all manner of code abominations can be hidden and compartmentalized. Various components can be improved and cleaned at will, without rippling out destructively throughout the codebase.
This means some shortcuts are not allowed, and time to delivery will increase slightly. But those are the shortcuts that create spaghetti-code and tend by far to incur the greatest debt. So I see this simple minimum bound as a good 80/20 tradeoff for maintainability.
When we work hard to write elegant code, we discover the patterns that allow us to write better code when crunch time comes.
Since reading code is infinitely harder than writing code, the next step that clever coders need to think of is not for themselves, but for others who will continue to work on what they do if they truly want to be working on other new and interesting things.
I haven't found my code gets to be automatically "hacky" if it "works" and isn't obsessed with elegance first. It's usually simpler and approachable by a greater number of decent programmers that can continue on it.
97% of the time the performance worries we have will never come to fruition, software rarely gets that kind of pressure, and very few frameworks (I hope) are that brutal.
Hiding what I do in a clever line of code that slows someone else from improving it, or taking a small 10-40 ms hit for something far more readable and editable. If it's a part of the logic that needs further optimization later, it's ready and willing be improved easily.
One stinking reality is software is just like hardware -- it will always hit its limits and fail. You can be reasonably kind to your future self, but not really prematurely optimize everything before it happens, and instead of making our own lives easier as developers, it should really be about the end users first.
Especially in startups, you never have time to take care of technical debt. In practice, technical debt is dealt with "on the side" and teams that don't do it will end up in situations in which simple features or pivots are extremely hard to execute, which will lead to lost business opportunities. I was at some point in this situation due to a minor pivot that should have been painless but wasn't, with the solution chosen being to rebuild the product from scratch. This is why I've been telling my colleagues to always leave the codebase in a better state every day, even if that means just renaming a variable. Sloppy code also suffers from the broken-window effect.
What you really want is to write elegant code that works and that's delivered on time. And the greatest trait of a senior developer is not "cutting corners" but rather simplifying the problem. That's not the same thing, as the simplification of a problem often leads to more initial work.
"The ability to make those decisions (of where to cut corners), often mid-project, is what separates veterans from rookies."
I don't have the kahonas to call myself a veteran but certainly, I can now relate to this. I shall be keeping this article to forward to colleagues at appropriate times in the future, along with Joel@FogCreek's article on why re-writing software with the same guys, the same skills and a simplistic - albeit optimistic - mindset is suicide for your application/service/software.
Real code that "you" didn't write is messy. It takes a long time to understand the history and context of any sizeable codebase. Perhaps that legacy feature is there to serve one customer, but it's an important customer who is about to upgrade to a more comprehensive plan. Perhaps that 100 line function is the only way to really handle some arcane API without muddying up the rest of the "better designed" parts.
Code you don't write is the hardest code to maintain, whether it's elegant or not. Code solves business problems, so without understanding the business or the specific problem it's trying to solve, you are not educated enough to make a snap judgement about it's "design" or "technical debt".
I say it all the time, code is not cement. It can be changed. Good engineers find the constraints, change the parts that are hard to understand and maintain, and keep everything up and running.
There is no "right way", or magic set of patterns that are going to save you from the realities of a user facing product.
The bad thing about our industry is that too many people take shortcuts and don't tell anybody about it or keep any records to assist in fixing the weaknesses that are introduced in the software. Sometimes this is malicious because a developer is trying to give the appearance of being a mythical 10x developer.
In fact, I believe that the vast majority of so-called 10x developers are actually frauds. Most people are unlikely to encounter such a developer outside of the blogosphere and conferences, unless you are fortunate enough to work for one of the big technology companies like Facebook, Amazon or Google, who have the resources to find the real McCoy.
I would recommend keeping an open mind and looking for programmers to work with that are able to teach you and that you collaborate well with.
If you find yourself being self-righteous too often when coming home from the job (or on the job) its time to find new folks to work with.
A quote by Ethan Marcotte (the guy behind responsive web design) sums it up nicely for me: "Designing for the web is like building sand sculptures."
Yes, there are certain situations where you should double-down on engineering, but it's wise to see if what you're building will even be around in a few months. Not to mention, a lot of the code I write is obsolete by the time it ships (either by virtue of my skills improving or the framework/language I'm using upgrading). A guy I have in my Skype list has the most foretelling status: "you're writing legacy code."
Whether you plan to stick around long term matters here. Are you just pumping some crap out at a 6 month contract and don't want to hold up any sprints? This is a good approach. Are you working at some stepping stone type company where you don't really care and are looking for something else?
If you're at a company you love and you plan to be there a while, you really should make a more concerted effort to build better stuff. The shortcuts you take now will catch up with you later. Also Technical debt is a real thing.
Hacking it and "Getting it done" for some hyperactive project manager who doesn't understand tech is fine and dandy for the short term, but if you stick around long enough you'll pay for those sins.
The point: Context is king in this matter, it sounds like the OP doesnt work on things that really matter much (and hence the cost of a bug is miniscule)
1) Over-engineering is a separate discussion to elegance, let's not mix them. 2) You can be both on-time and elegant - it's not one or the other, like the title implies. 3) Often, if that fails, the deadline can be moved without any real consequence. 4) Consistent failure to be elegant "because deadlines" is probably going to make later deadlines harder to meet - it seems like a bad cycle to start.
There is definite truth in the fact that the decision to minimise (as far as is possible) technical debt accrued throughout a project's journey to release should be carefully considered if the actual, "real world", goal of the project is in some way time sensitive.
However I take issue with a number of statements made throughout the article; Namely:
- "With that said, "best practices," however you define it, should be a natural coding standard for any decent developer". In my experience this is simply not true. A lot of tried and tested best practices (SOLID etc...) are not necessarily intuitive at all. How would I, as an unexperienced programmer, have known that the "new" keyword is often a code smell, if someone had not told me?
- "If the client needs a Christmas promotion, and you deliver the best product in the history of promotions -- on December 29th -- it's worthless". This is true. It is also an exercise in reductio ad absurdum. The author chose an example with a hard deadline, which is unusual. Furthermore, to turn that example on its head:
No Christmas promotion is due to arrive on Christmas day.
It might, instead, be due to arrive on December 1st. If
the outcome of releasing the promotion on time would be
worse than if it was delayed by a week (i.e. The project
had major technical flaws), then delaying by a week would
be the correct decision. This is a decision that business
men/women should make in conjunction with engineers. Is
a working promotion on the 7th of December worth more than
a non-functional one on the 1st? Probably.
Mostly, though, I agree with the idea that engineers should be conscious of the broader "why" & "when" of their work, rather than just the "what" & "how". This is often achieved through communication, which should (IMO) be the true takeaway from articles such as this.EDIT: Obviously being late on a project is never good. This is why contracts should have penalty clauses, which should be factored into the decision I discussed.
Second - it is not one or the other. It needs to both work and be comprehensible. Delivering "non-elegant" code that works though, is job half done.
many people with little familiarity or experience with actual philosophical work seem to believe the razor urges you:
"the simplest explanation is more likely to be correct."
however, the razor actually says:
"do not multiply entities beyond necessity."
the point here--and it's not just for programming or logic--is that you can make your explanation as big/small complex/simple as you want, but if it's more complex than it needs to be to adequately explain what you're talking about, ockham says: cut it down.
if my theory has 1000 elements, your theory has 2, and we predict slightly different results to some experiment, ockham has nothing at all to say to us. ockham's razor is only useful regarding predictively equivalent theories. if ockham were saying "keep your predictions simple stupid," his razor would be trivially bad, and its implications trivially false.
similar to code: if it doesn't work, its elegance is irrelevant. virtues like parsimony, elegance, or even scalability and the like are built on top of actually doing the job in the first place.
edit: p.s. people are attacking the article for its strawmen, or for being too simplistic re: speed vs quality. i don't see how either of those get in the way of the author's fundamental point: don't overengineer, or mistake "objective quality" (???) with what's called for in a project. if i'm buying a $100 laptop, i'm upset that it doesn't have a haswell i7, but i'm not immediately wondering why the laptop's processor is so bad because it doesn't have an i7. ideas need to be kept straight.
<http://thedailywtf.com/Articles/User-Rejection-Testing.aspx>
"There are only two options here: either you did your job and the application is correct, or you’re an incompetent and it’s wrong."
Not all working code will be working in 5 years.
Really, prove it. Oh wait, writing tests is 'over-engineering' and "Best Practice", you don't do that. Do you use WinZip every night rather than use an SCM? yes? your a f
*king idiot.If best practices make your code take longer to develop then one has to question whether they are best practices at all.