Pro tip for those who always do these inane headline reversals: 100 other people also think of the quip, but they all come to their senses and realise it isn't worth posting.
> 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.
Just reminded me of a story about the designer Peter Saville (from the film 24 Hour Party People)
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."'
I managed to read the article before it went down, it was a pretty good read. Hopefully it comes back up soon.
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.
Your equation is quite incomplete. You must add the cost of making the code problem-free without cutting corners...
Something like that:
Revenue(no cutting corners) - Cost(no cutting corners) < Revenue(cutting corners) * P(no problem) - Loss(cutting corners) * P(problem) - Cost(cutting corners)
It's also worth noting that he's talking about things like "if your client needs a Christmas promotion and you deliver on Dec 29". This sounds about right. But if you're in a time-sensitive situation and your code will only be used for a brief period of time, your business needs are probably very different than a code-and-maintain-forever situation (e.g. a SaaS offering).
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.
> But if you're in a time-sensitive situation and your code will only be used for a brief period of time, your business needs are probably very different than a code-and-maintain-forever situation (e.g. a SaaS offering).
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.
I don't know that sophisticated spreadsheets are trivial to create. I've seen way too many weird, buggy and outright broken spreadsheets to believe that.
I agree with you we should strive to make trivial things simple to write, and I also agree many programming tools fail at that, but I don't agree the software we write (in general) qualifies as trivial. Even some spreadsheets don't qualify. People writing them often make a horrible mess of it, unless they truly are simple spreadsheets.
haha, I am sure there are many buggy spreadsheets out there...
I didn't mean to suggest that it doesn't require knowledge and effort to put together a sophisticated spreadsheet but relative to building even the simplest web application, spreadsheets are a breeze.
As bad as the spread sheets can get it's helpful to have the business side make them. Even if you do have to implement a 'real' solution at least you now have a prototype to go off of
This is a problem, and I've long mused over possible solutions. Every time I think about it, I come to the conclusion that the solution isn't a new approach to software engineering, a new language that blows away every paradigm, some abstraction that promises to make app development simple, or any other silver bullet.
Indeed, the only "solution" I see is a solution in just the loosest sense. Namely: We continue incrementally improving languages, frameworks, libraries, and coding practices--a process that has been going on since software began. That's a disappointing answer, but I think it's the only answer, and I'll explain why.
The complexity of the tools we use is a result of their great flexibility and power. For example, a Ruby on Rails app is a huge agglomeration of technology, including HTML, JavaScript, jQuery, CSS, SCSS, Ruby, the Rails framework, and usually a number of other, project-specific technologies. All of these technologies must be learned (no small task), and their quirky interactions with each other must be managed. One sometimes thinks, "There must be a better way! There must be some abstraction that can hide all this technology." But I challenge anyone to design such a thing.
Each of those technologies is mature. Each one has had tremendous effort put into designing the simplest possible API without sacrificing power and flexibility. Even technologies with a lot of baggage, like HTML, CSS, and JavaScript, have seen substantial improvements in recent years. Given that, does anyone think they can do better? Can anyone do everything these technologies do and present a radically simplified API? Remember, you can't sacrifice power and flexibility and just support the most common use cases. Almost any application will need at least one thing that's unique or idiosyncratic.
This is why people use Rails (or similar tools) for custom apps instead of Wordpress. Wordpress has many off-the-shelf plugins that provide commonly-needed features. From a site maintainer's perspective, Wordpress abstracts away most of the complex web programming inherent in those features. But for serious app developers, that's almost never good enough. The plugins never seem to work exactly as you need them to, and they rarely talk to each other the way you would like. A short ways into the project, you're forced to either hack up your plugins, which is a maintenance disaster, or code your own plugins, which means you're really just using Wordpress as an inferior Rails substitute.
A good tool does not need to throwaway existing frameworks or paradigms in order to deliver. I am a firm believer in using what's already proven.
>The complexity of the tools we use is a result of their great flexibility and power.
This may see like a strength but it is actually a massive weakness. Existing IDEs attempt to be all things to all people and as a result are not actually delivering the best that they could if they were more focused.
>Given that, does anyone think they can do better?
Download our software (check my profile for link) and spend some time playing with it, you might just reconsider that position :)
Or join the newsletter, we are coming out with an exciting update that is basically going to attempt to combine wordpress-like application composition within an IDE.
I looked at Crudzilla. (Is that the product you're referring to?) I don't really have the time to test-run a new framework right now. Perhaps you could give a quick overview of how you're solving the key problem I identified above? To recap, my argument is: The web stack must be flexible and powerful, because nobody's abstraction layer can anticipate the weird, idiosyncratic little needs of most real-world projects. Thus, the flexibility and power of the dominant tools, like Rails and Django, can't be sacrificed in the name of simplicity. Or if you do make that sacrifice, your tool will only be useful to the tiny subset of applications that fit into your narrow definition of how an app works or what it does.
I see intrinsic informational limitations. Software developers and managers learn, but they also forget. Also, technologies change, so knowledge "rusts" over time. So the rational ability to make information-based trade-offs is dynamic. Now, think about competition and you can see why this gets complex. I wouldn't be surprised if there is a system dynamics paper on this phenomena.
And this is why I won't work in any industry where "a time-sensitive situation [where] your code will only be used for a brief period of time" is business-as-usual. Like, for example, the games industry.
Definitely, thanks for adding in those terms. I kept it simpl(er) and left out the extra terms because I think that those two terms (benefit from cutting the corner vs loss of not cutting the corner) are the major factors when making the decision as they can have big effects on the business itself, while the other terms are just shifting around a bit of extra money/time. Maybe. It would depend on the corner.
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.
Reducing software development practices down to these cute catchphrases is a bit disingenuous. If you're writing throwaway code for a client with loose constraints and a tight deadline you'll write code differently then you would when you expect to maintain a long term relationship with a client who expects a high degree of correctness. I'm tired of these trite articles espousing some cute mantra holds as if it's some universal property of software development.
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.
> Almost all similar articles I read, are used as some kind of self justification
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!")
Nerds ( also one myself ) tend to suffer from the over-generalization fallacy. Whereby they think that their local experience generalizes to a much larger scope. For instance, If you read most of HN programming articles with the prefix "when writing code for a web startup" they make sense, if drop that prefix they stop making sense. It also helps to keep in mind that advice that holds in the startup world can be quite toxic when applied elsewhere in the industry.
Definitely. I disregard most 'engineering' advice I read in HN threads for exactly this reason. Most of it falls into the "I don't believe it's an issue in my 1.5k LOC app, so it must not be real!"
The worse thing is that he's saying "you shouldn't spend extra time to do the right thing", and then tries to justify that by giving examples of people doing the wrong thing. Nobody is calling spending weeks writing a caching layer for a 20-row database "elegant" or "best practice". That's a complete straw man.
Sort of. That just means they endorse an Agile approach - ship bare minimum features early, and continuously improve. Not ship bare minimum features early, and who cares about how its designed. The whole over-engineering argument is a straw man, because over-engineering is just as bad as under-engineering. Neither one is desirable in professionally written software.
"Nobody is calling spending weeks writing a caching layer for a 20-row database "elegant" or "best practice"
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...
I was involved in a software development project last year, in which the entire remote development team of 20 people got fired, because the software architect implemented a bunch of best practices, design patterns and state-of-the-art frameworks, without any thought as to how this would impact development time. The problem was that he was the only one who was familiar with most of this stuff, so there was a very steep learning curve for the rest of the team, who one by one either dropped out or became totally unproductive. After 18 months in which he burnt his way through the budget, with very little to show the client by way of results, the project was drastically scaled back. So this is not just about straw men. People like this really do exist.
Things like best practices become more important the larger the team. Are you suggesting the project would have been more successful if they had been less concerned about code organization and unit testing (or whatever you mean by "best practices")?
The problem was not the use of best practices per se, but the uncritical use of these practices. For example, it's good to use design patterns, but not every pattern is appropriate to every situation. We got to a point where it might take a full day to make a change to a table, and then make the corresponding changes to all the various repositories and entities. Similarly, it's good to use the latest technologies, but using beta versions of unpublished frameworks was a bit over the top, both because documentation was scarce and because they were still buggy.
Meta comment: The article:
(a) covers enough relevant topics
(b) using rather ambiguous terminology
(c) while offering sharp opinions
(d) about a world of pain
(e) that we all experience
(f) but can never be solved in general
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.
"This way, though you may be accruing technical debt, you are also accruing revenue immediately, and you can reconcile the debt over time."
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.
Yes. "Reconciling technical debt" does not fit in well with the philosphy of the title. Why refactor when you can just write more code that "fucking works".
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...
The code is elegant AND it works, so it'll beat the one that just works every time.
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.
> And of course, that dirty code will be refactored later on.
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.
I partially agree... normally this is a symptom of someone selling it as being something super simple to do for the managers.
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 :(
... and as expected, it's always the manager, not coding types that state this so aggressively. I know the servers are beefy at OmniTI, so I suspect there might be a code problem related to handling the load.
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)?
I am a coder type, and I mostly support the sentiment.
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).
> 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.
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.
The difficulty of changing code is directly proportional to the amount of code that has to be changed. This is almost tautological.
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.
My experience too. If they balk at the price/time it will take to build a quality solution, let them go with a lower bidder. They'll come back and willingly pay this time.
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.
Hmm. It's not that I disagree as such, but more that people who talk like this tend to be terrible cowboy coders. But I agree that developers can be annoyingly fussy about things that actually do not matter at all. No argument there.
Plus elegance is often a bullshit notion and when people say it what they really mean is "what I personally like".
This is a far better metric. Elegance, while a worthwhile goal, is at its best a vague notion and at its worst, a guise for all sorts of leaky abstractions.
I adore elegance, but over time have come to understand that edge cases tend to creep into those "leaky abstractions," and you can find yourself unraveling things, at least partially, in order to solve bugs.
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/
Yeah, my team has inherited similar code from one particular Boy Wonder. One example was a service that imported JSON from third parties - it was unit tested, but the architecture was so poorly testable that it had mocked dependencies three levels deep. We also picked up a home-rolled reporting tool that was an instant injection of significant technical debt.
There's basically just one golden rule: a programmer tries to minimize his total amount of work (because then s/he has more work capability available for any of the more interesting stuff).
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.
There's a sub-species of programmer that loves /programming/. Not getting things done with code, but the act of writing code itself. They'll happily over engineer and refactor things for eternity if left unchecked. Leave them alone for two years, come back and ask if Hello World is done? "Not yet, I'm working on optimizing the text to speech plugin api". It's to these people that this article should really be speaking to.
Definitely. And there are programmers that love code for its own sake, rather than for what it does. They seem to be motivated by "what would another programmer think if s/he saw my code?" -- rather than, "what will the user think?"
As a database architect I disapprove of this message.
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.
This sentiment is why I only work in domains that value high-quality work. My definition of high-quality is something that's done well enough that it doesn't have any obvious issues, based upon the current product vision. It should be designed well enough that we can move onto subsequent requirements afterwards.
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.)
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 ;)
> 90 percent of the time, if you make it beautiful, it will already be fast.
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.
I would argue that the reason you are choosing to recouple things, is because you fall into that 10%. Most problems can and should be solved first correctly, second elegantly, and you will find at that stage it is "fast enough". It's so rare to not find that to be true, overall (i.e., some industries, such as high speed trading, there is no such thing as fast enough, but they're writing < 10% of the total amount of code being written), that yes, I'd say the quote is accurate, and the exceptions fall into that 10%.
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.
Different projects call for different standards of quality. A Christmas promotion might as well be spaghetti code if that's what it takes to get it out the door in time, but if you're doing say an RTOS or a foundation library then you'd better take the time to do it the right way and respect the craft.
They have a CEO, a CAO and a VP of business development but nowhere on their about us page does it list a CTO or ANY senior level "engineer". And this is why their company doesn't value doing development right.
He presents so many false choices it's not even funny. (also we're adults, either you really want to fucking write it, or don't - stars don't help)
> 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.
Well this is a developer working on Christmas promotions. It's a bit difficult to take him seriously. Let's see how he copes on a smallish 20,000+ person hour project scope with a 5 year+ expected maintenance lifetime and then revisit the discussion.
I think that is the number one purpose to make software work but doesn't have to entitle a programmer to do stupid things. The quote from Ralph Johnson states this:"Before software can be reusable it first has to be usable.". In my mind, every real programmer likes to write pretty codes because after reaching higher levels trivial problems are so boring and writing flexible code kills two birds with one stone. Writing disposable code is another case. If you aren't interested in programming and don't enjoy it, you shouldn't have to choose this profession.
173 comments
[ 3.4 ms ] story [ 198 ms ] thread/cheap shot, it's probably just a wordpress site without caching plugin or whatever the usual magic is to make it work.
https://twitter.com/postwait/status/431117981922631681
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?
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
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.
I agree with you we should strive to make trivial things simple to write, and I also agree many programming tools fail at that, but I don't agree the software we write (in general) qualifies as trivial. Even some spreadsheets don't qualify. People writing them often make a horrible mess of it, unless they truly are simple spreadsheets.
I didn't mean to suggest that it doesn't require knowledge and effort to put together a sophisticated spreadsheet but relative to building even the simplest web application, spreadsheets are a breeze.
Indeed, the only "solution" I see is a solution in just the loosest sense. Namely: We continue incrementally improving languages, frameworks, libraries, and coding practices--a process that has been going on since software began. That's a disappointing answer, but I think it's the only answer, and I'll explain why.
The complexity of the tools we use is a result of their great flexibility and power. For example, a Ruby on Rails app is a huge agglomeration of technology, including HTML, JavaScript, jQuery, CSS, SCSS, Ruby, the Rails framework, and usually a number of other, project-specific technologies. All of these technologies must be learned (no small task), and their quirky interactions with each other must be managed. One sometimes thinks, "There must be a better way! There must be some abstraction that can hide all this technology." But I challenge anyone to design such a thing.
Each of those technologies is mature. Each one has had tremendous effort put into designing the simplest possible API without sacrificing power and flexibility. Even technologies with a lot of baggage, like HTML, CSS, and JavaScript, have seen substantial improvements in recent years. Given that, does anyone think they can do better? Can anyone do everything these technologies do and present a radically simplified API? Remember, you can't sacrifice power and flexibility and just support the most common use cases. Almost any application will need at least one thing that's unique or idiosyncratic.
This is why people use Rails (or similar tools) for custom apps instead of Wordpress. Wordpress has many off-the-shelf plugins that provide commonly-needed features. From a site maintainer's perspective, Wordpress abstracts away most of the complex web programming inherent in those features. But for serious app developers, that's almost never good enough. The plugins never seem to work exactly as you need them to, and they rarely talk to each other the way you would like. A short ways into the project, you're forced to either hack up your plugins, which is a maintenance disaster, or code your own plugins, which means you're really just using Wordpress as an inferior Rails substitute.
>The complexity of the tools we use is a result of their great flexibility and power.
This may see like a strength but it is actually a massive weakness. Existing IDEs attempt to be all things to all people and as a result are not actually delivering the best that they could if they were more focused.
>Given that, does anyone think they can do better?
Download our software (check my profile for link) and spend some time playing with it, you might just reconsider that position :)
Or join the newsletter, we are coming out with an exciting update that is basically going to attempt to combine wordpress-like application composition within an IDE.
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:
Symbols: p = probability; b = benefits ($); c = costs ($); P = problems; ~P = no problems; C = cutting corner; ~C = not cutting corner.Rearranging to:
Then you get a simpler form: 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.
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.
Basically, the same thing as he's saying.
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.
Its a bit like Scientology, but without the crazy.
Oh no, wait .. there's plenty of crazy.
http://deliberate-software.com/quality-is-future-speed/
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.
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...
When we work hard to write elegant code, we discover the patterns that allow us to write better code when crunch time comes.
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 :(
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.
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.
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.
Plus elegance is often a bullshit notion and when people say it what they really mean is "what I personally like".
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/
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.
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.
"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!")
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.)
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 ;)
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.
> 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.
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.