15 ms·
Code quality: a concern for businesses, bottom lines, and empathetic programmers
- adam_arthur 5y agoFundamentally, poor code leads to destruction of business value. The next generation of software creators will likely look back on a lot of current practices as barbaric. Not practices that people advocate, but practices they actually do, whether by accident or through lack of knowledge. I inherited a project once that had been built very quickly, over the span of a few months, and provided immediate value to customers, but the codebase was awful. Not just in a syntactical way, but structurally flawed, like using poorly bounded abstractions all over the code in a way that made it difficult to grasp and work on effectively. It took 2 years of rewriting a bit here or there to finally get it to a decent state, but even today some of the poor abstractions live on, due to the effort to change it in flight. Truthfully, looking back on it, that was a good candidate for a total rewrite. But we all know you put your neck on the line and have to have strong and tangible justifications to get product on board with rewrites. But at the end of the day, looking back, there was likely multiple years of employee time wasted due to the poor design decisions. That's why I always cringe a bit when people advocate putting out something quick and dirty for business reasons. Usually the damage you cause in technical debt is difficult to reverse, and you end up wasting a lot more time in the long run. If you're cognizant of the tech debt you're introducing, and have a clear plan to rectify, that certainly helps a lot. Most of the time technical debt is introduced through ignorance, rather than through an explicit shortcut decision. That kind is more sinister, as you aren't even aware of the burden that you've created. It will be a nice surprise for the next developer to find out! Of course, this moreso applies to SaaS where your product lives forever. A one off deliverable would have different tradeoffs.
- orev 5y agoIt sounds like that “bad” code was good enough to provide enough business value to keep the company going and you employed for at least 2 years, so I’m not sure your assessment is accurate. Maybe you mean “destruction of value” in terms of opportunity cost, but that’s much more abstract and very hard to truly measure. Businesses run on cash flow like blood. Once the cash stops, it dies right then and there. It doesn’t matter if a better design will only take a few more months if you can’t make it to tomorrow.
- ToJans 5y agoI think you did a great job and are selling yourself short. Your story implies that you were unable to architect for the requirements upfront, because they probably were too volatile. You provided business value ASAP, and, if tech debt was ignored in the beginning, it's probably because there were good reasons to do so. Your job is to architect a good enough system based on the requirements you have and the assumptions that you make. It might help to document these using something like ADR, but this depends on your context. I found out about a decade ago that documenting these assumptions and estimating impact/risk and potential mitigations might help to motivate your choices. In hindsight it's always easy to tell why an existing architecture was the wrong choice, but this is a fallacy, as architecture is based on a snapshot, and you have to find a delicate balance between changing architecture and providing business value all the time. I usually advise against big rewrites, in my own code I try to aim for "making parts easily disposable or replaceable"; if the mess is abstracted away it doesn't seem to matter in 90% of the cases. In the 10% where it does matter, you'll end up refactoring anyway because it's probably your core domain and your insights and learning are evolving faster there, and you will inflict a lot of pain on yourself if you don't respect proper development practices in those parts.
- onion2k 5y agolooking back on it, that was a good candidate for a total rewrite It's virtually impossible to rewrite a large scale application where the only documentation is the code, especially if the code is bad enough to warrant a rewrite. You will break things, lose features, potentially corrupt data. Total rewrites of unspecified, untestable, poorly understood systems should be the last resort. That said, the system was still running with customers using it 2 and a half years later, which is a very strong signal that it didn't need a rewrite after all. It sounds like you got it right - refactor as you go is usually the best way out of a bad codebase.
- quickthrower2 5y agoA way around this is a side by side test of a lot of the functions. Maybe generate tests form system 1 outputs and use them to test the greenfield system. Where they differ you then decide where the bug is.
- adam_arthur 5y agoYeah this would work very well for certain types of apps. An API would be easy to validate with this approach, for example.
- adam_arthur 5y agoYeah, definitely a good rule of thumb :) But in this case a total rewrite would have saved time, and improved velocity. Looking back on it. It was impossible to really know at the time, though. Fortunately this project was more presentational than business logic heavy, so missing functionality would have been easy to notice when testing (all visual). I wouldn't agree that running is a sign of success necessarily. There are a lot of running legacy systems that get beaten out and replaced by competitors due to low development velocity. Of course a lot of that comes down to the culture of the org and so on. But in a lot of cases architectural flaws are at the root of it. The real question with a rewrite is a cost-benefit one. And it's based on estimates so there should be a lot of room for error.
- quickthrower2 5y agoIt feels like for that to work you need to be managed by people that appreciate this always to the top, to the point where this affects hiring, on boarding, etc. you also need a compatible business model. Quality code where you can get away with it not being is a fragile thing (in the fragile/anti fragile) sense. I wonder if the hill to die on is getting a good architecture that minimises the impact of bad code not as bad.
- adam_arthur 5y agoYeah, the strength of a business is tightly coupled to the principles and culture underlying that business. I've found many speak to caring about code quality, but in the startup scene it seems quite rare for people to actually live it. People obviously talk about linting and syntax, but appropriate separation of concerns, abstractions, coupling and so on are way more impactful. Unfortunately those things are also difficult to quantify without knowing the context. Hard to train people on these things, it takes a lot of thinking and experience. But common threads around best practices and principles start to appear. I've mentored/managed many people and always enforced quality as a cultural element of our teams, and you can see that those kind of principles become "viral" in a sense. They get adopted by those you work with, and they impress those principles upon others down the line. To your last point, I do think proper separation of concerns is probably the most important principle. Such that you can easily fix any mistakes that are made. Microservices are a good vessel for this in theory, but it's possible to build a distributed monolith in microsevice form. e.g. exposing low level apis and having a high level of chattiness. So yeah, those boundaries need to be well defined haha.
- honkycat 5y agoSeconding this: Poor code quality can eat a business alive. We cannot change ANYTHING. ANYWHERE with our current stack, due to years of product micro-managing engineering and never having room to re-build or improve things. Also just bad practice from inexperienced engineers with 0 leadership. We tried to do the "microservice" thing but we never ACTUALLY got the bandwidth in our sprints to do all the work that is needed, and product never backed us up to deprecate old APIs that were too tightly coupled with a ton of other functionality to adequately refactor( Also the code quality was so poor it is hard to reverse-engineer the API ). So we have the new APIs sitting dark. Here is another fun knock-on effect: Everyone who joins quits as soon as possible, because their job is nothing but fucking around with shitty code written by someone half-way to Tahiti by now. Heck: I have had to convince myself to stay several times now[0]. I ask myself: Is what I am doing worth my time? Am I growing? What am I going to tell my next employer? Will saying: "I spent a year nursing a dying monolith built on a mountain of bad practices" get me my next job? I hope I can help heal this place and get it to a better position, but better people than I have failed. 0: I had a run of crappy gigs and I don't want to switch since this is tolerable so far.
- TeMPOraL 5y ago> That's why I always cringe a bit when people advocate putting out something quick and dirty for business reasons. Usually the damage you cause in technical debt is difficult to reverse, and you end up wasting a lot more time in the long run. > Of course, this moreso applies to SaaS where your product lives forever. The thing is, though, SaaS products don't live forever. In fact, they have ridiculously short half life :). Sometimes it's the effect of the problems of code quality (in which case the company dies prematurely, or the product gets rewritten in a new tech stack). But often enough, it's the cause. Plenty of startups are eyeing for an exit from day one. They fly their "rocket ship" the Kerbal way: building the vehicle out of old oil tanks welded together and filled with rocket fuel, and having a bunch of green men on the outside desperately plugging every leak as the rocket tries to break out of the atmosphere, praying it'll get to the Moon before it explodes. There's a top-down pressure to move fast, instead of worrying about code quality, because there's only one goal: exit or bust.
- denkmoon 5y agoCurrently in a job where code quality is not even considered a low priority, but a waste of time. I'm now on week 6 of attempting to complete a "simple" HTML page update. Piles of documentation around, but none of it useful or relevant. So I have to work out how the system works from looking at code, but the code is so damn bad every time I think I know what I need to do, it turns out to be wrong. The people who know what to do left many years ago, of course.
- yxhuvud 5y agoI would recommend you get into the habit of making small improvements that fly under the radar while doing other tasks. It may not fix big architectural issues, but it will still help with making the code base more understandable and less brittle.
- denkmoon 5y agoThe improvement I'm making is searching for a new position :) Can't help those who won't help themselves.
- gunshai 5y ago:( I am currently in a similar situation but it is all scripts on an industrial data historian intertwined with DB updates. When the previous hire gave notice he had 2 weeks to document what took many people more than 5 years of effort to piece together. I won't call this hell, but at least my manager and boss have very tempered expectations. Edit: I'll be sending this article out to my co-workers in the morning with some snippets.
- goto11 5y agoAll developers know code quality is important for long-term productivity and efficiency. But from a management perspective the problems are: - There is no way to measure code quality - There is no way to measure developer productivity - Code quality is quite subjective and the field is driven by opinions and fads. I have seen many examples where developers thought they were improving code quality by implementing some pattern or architecture but actually made it worse. So for non-technical leadership, it is hard to know if "extra time" spent on code quality actually improves anything or is a waste of time and money.
- nradov 5y agoThere are static analysis tools like SonarQube which can measure code quality to an extent by looking for patterns that typically represent defects, or at least maintainability problems. But they usually can't detect deeper design flaws.
- goto11 5y agoSuch tools don't measure any objective code quality, they just measure to what extend the code conforms to some criteria defined by the people configuring the tools. Do these criteria correspond to proven long term maintainability benefits, or are they just unfounded opinions?
- nradov 5y agoThe tools will catch issues like excessively long methods or use of "magic numbers" instead of named constants. Most programmers would agree that those are maintainability issues, although I don't know if it's ever been proven. You can disable any rules you don't want.
- goto11 5y agoYeah but such rules are still just unfounded opinions which have been codified and human judgement have been removed. What is an "excessively long" method, for example? If a method conforms to good design principles like separation of concerns and low coupling high cohesion, no particular length is too long. If a tool force you to split a cohesive method into multiple methods, then you have increased accidental complexity. So the tool will tell you you have improved some arbitrary "code quality" metric, but actually you have decreased maintainability.
- DeathArrow 5y agoI think that you should assess costs/benefits. If you're a small startup and you can only afford to pay five programmers for a year, that means you will concentrate on coming up with a MVP as fast as possible, even if that means having to do a complete rewrite soon. Otherwise, you won't survive. If you are a big corporation and can afford more resources, I guess it pays off to think about code quality from the start. If you are doing some project for fun or as a POC, it doesn't pay much to spend time on writing quality code. I think that writing modular code is paramount. That way you can replace it little by little if needed.
- JanNash 5y agoPersonally, when I’m writing for fun, I especially try writing quality code. There, I can reason about it, learn it and do it without management breathing down my neck due to business constraints like time and money.
- d3ckard 5y agoModular code is quality code. Separation of concerns and ability to rewrite parts of it independently is paramount to whatever else code quality metrics your team decides to use.
- davedx 5y agoYes! You pretty much sum up my own thoughts on this subject too. I'd add that for doing projects for fun or POC, it still pays to pull in something like TypeScript if it's a JavaScript project, even if you're not overly fussy with the typings -- the benefits will outweigh the drawbacks, and you never know when a POC will turn into something bigger. Choose some sensible tools with a good balance of structure and pragmatism and write some damn code. Keep it modular. Don't spend weeks obsessing and agonising.
- makeitdouble 5y agoIf you only have 5 devs, you can't afford to deal with bugs and crisis all the time. I'd argue code quality matters a lot more than in a company with already a lot of inertia, for which a week or two of delays on a new feature matters a lot less.
- nikanj 5y agoManagement cares about code quality. Management just cares about profits, deadlines, customer acquisition etc about a million-fold more. They'll always choose getting that deal over getting that good code quality.
- ken47 5y agoYes, the drive for profits extinguishing the beauty from life yet again. If only the prevalent managerial culture was more philosophically sophisticated...
- drewcoo 5y agoI still don't know WTF "code quality" means. Do I need to buy an even-longer book to really understand it? Where can I hire someone to properly consulant-splain it to me?
- cjg 5y agoWhat does it mean for a novel to be of high quality? It's not easy to define and it's somewhat subjective, but equally there are definitely things that are not acceptable. Some things that can help move towards higher quality code: - avoid duplicated code - especially when it's similar but subtly different - variable names that mean something useful - lower code nesting - shorter functions - fewer parameters Many of these things are focused on improving how understandable / maintainable the code is. But, as for novels, there aren't just rules you can follow blindly. Perhaps this helps: https://garywoodfine.com/what-is-clean-code/ https://garywoodfine.com/what-is-clean-code/
- zgs 5y agoOver the years I've worked in many different places both small and huge -- only two considered code quality as even remotely important. One being an open source project and the other was very heavily involved in open source. All commercial code of any reasonable size that I've seen has been dreadful. Complex interdependencies, little regard for consistency and way too much legacy laying around to surprise the programmers. I no longer believe that any closed source code can be expected to execute properly.
- TeMPOraL 5y agoThe way I see it, it's not the "closed source" part that's the biggest problem. The root cause is time pressure: you can only spend so much time on X, because we really need to get Y working - and there won't be time after Y is done to revisit X, because we'll need you to work on Z then. Closed source strongly correlates with business, which strongly correlates with paid employees, competition, investment - meaning time pressure. Open source strongly correlates with volunteers working on free solutions in search of adoption - meaning little time pressure.
- hsn915 5y agoI recently had a conversation with my "technical" manager. He basically told me in no unclear terms that it's "business as usual" and totally expected that you just fumble around the codebase not really understaning what's going on, write or fix the code while feeling frustrated and demotivated, push changes that are full of obscure bugs, wait for users to run into these bugs and report them, then try to fix them while still not having a clue what's going on in this codebase. He used the fact that this product "makes money" as a reason that we should not be caring about making the codebase any easier to understand. How is one supposed to respond to this kind of mentality?
- firebaze 5y agoNot implying this is the case for your situation, but occasionally there's another potential perspective: suppose you're a junior dev, not much experience yet, and you don't really understand the business case at hand. Less experienced technical managers may resort to a similar wording as the one you just mentioned to prevent that dev to refactor something without really having a good idea how it should be in the first place. Again, not saying that's what's going on in your example, but I've seen this more than just once.
- hsn915 5y agoYou don't tell a junior that it's business as usual to push code without understanding what you're doing.
- firebaze 5y agoIf the junior doesn't understand the reason behind the code, and if you're an inexperienced manager, you totally could do that. Sad, but from my own experience this happens. And sometimes it's the least bad way to teach.
- LandR 5y agoLeave.
- DeathArrow 5y agoCode quality means different things to different people. For some means applying the most obscure design patterns known to mankind and several layers of abstraction, for others it means Uncle Bob's teachings + GoF patterns, for others it means having pure functions everywhere, for others it means sticking to a strict style guide, for others it means following a particular set of conventions, for others it means writing readable and code, for others it means writing composable code, for others it means writing modular code, for others it means a mix of some of the above.
- DeathArrow 5y ago>Encapsulation. High-quality code is most often made up of self-contained components: one cannot change the behavior of a self-contained component by modifying something outside of it, nor does the component modify things that are external to it (within reason—it makes sense for a component to read and update a database if that’s understood to be its job). This saves development time because when a component needs to be fixed, updated, or deleted, programmers spend less time searching for external causes and effects. I am ok with it only if it doesn't mean "encapsulation in an OOP way" which means hiding state in many different places which leads to complexity.
- veltas 5y ago> Meaningless names, like `x` or `fn`, require the programmer to understand and remember extra layers of context while reading code that uses them. Reading code always requires context, which humans are actually very good at handling. And if you refuse to use short names where they suffice you will clutter the code. This feels like a false optimisation of our ability to handle context. What's more readable/understandable? /* Gets sum of numbers from 1 to n */ sum(n) { return n * (n + 1) / 2; } Or: /* Gets sum of numbers from 1 to largestNumber */ sum(largestNumber) { return largestNumber * (largestNumber + 1) / 2; } Have mathematicians been wrong for hundreds of years by using short symbolic names for their different variables? Imagine what maths would be like today if they were told they needed to use long names every to optimise 'context'.
- stinos 5y agoJust like with other design principles / best practices / ... this is context-dependent and there's a certain threshold wrt number of lines, number of variables, complexity of the function. Above which, in this case, n starts to lose its meaning and the OP's principle starts to make sense imo.
- ivanche 5y agoNow imagine your function is 50 lines long and has 9 variables inside. Ooops!
- veltas 5y ago"where they suffice" i.e. not always.
- TomSwirly 5y agoThe key idea is this: > require the programmer to understand and remember extra layers of context in sum(i for i in texts) I have no chance to forget what `i` is, but if I write: for i in texts: # A page of code without i in it. Then I'll just have no idea when I scroll down.
- lordgrenville 5y ago
- Zababa 5y agoI think instead of "encapsulation", they should use the word "locality". Changes are easy when the consequences of a change are local. Encapsulation may be a way of doing this, but not the only one. Encapsulation also has a lot of baggage from OO.
- felizuno 5y agoWhen you hold a hammer everything looks like a nail. When your job is to write code you think the entire business is the code. The UX org thinks the business lives and dies on design, the engineering org thinks the same about code, and the finance org thinks the same about budgets. None of these elements are optional but it's uncommon that any of them are actually what the business sells for money. Most businesses are not "technology first" businesses and at these companies you must not make perfect the enemy of good enough by obsessing about things like code quality or artistic design purity. Sure bugs can reduce customer facing quality, but not as much as completely lacking the new features that your team put off for two sprints so they could switch CSS preprocessors for the second time in 8 months. Look at all the companies founded by former programmers with perfect clean code who fail miserably at being an actual business. Then compare that to the number of successful businesses with absolutely junk tech that do silly things like appear in the S&P 500. It's not that I think garbage code is great, simply that over focusing on code quality is frequently selfish and benefits developers at the expense of actual customers.
- Marlondu28100 5y agoJjjj
- marcus_holmes 5y agothis. Our job is to write profitable code. Not beautiful code. There's a balance; too much tech debt and the code isn't profitable. But too little tech debt is not profitable either.
- kayamon 5y agoThat’s not your job, that’s just how you choose to live your life. You reject beauty.
- xboxnolifes 5y agoWhen you program as an art form, you get the salary of an artist.
- 5y ago
- game_the0ry 5y agoGreat read - I am a believer in code quality as well. Unfortunately, code speed is incentivized over code quality pretty much always. I'm going to be a little tongue and cheek, and a little real - I've learned to look at this as an opportunity rather than a threat. Sure, Mr Product Manager, I'll deliver your project in an unrealistic time frame, knowing full well you won't listen to my feedback. But, uh oh, there are changes you want next quarter and they are not easy to implement bc the code is not extensible. Now we are seeing defects outside of the well-tested happy path, not good. I may or may not be on a different project by that time. At least I can spin that project as a "win" on my year-end review. Good luck! And this new project is fuckin cluster, but that's a good thing bc gets who gets the job to fix it (this guy). Job security! To all the developers that will inherit my mess - I should apologize but I won't bc I have been in your shoes. Think of it as a learning experience, or character building exercise. Stay positive! :) ^^ Agian, tongue and cheek. Don't like playing that game, it sucks, but it's in my best interest to play the game.
- stickyricky 5y agoCode quality is context dependent. Libraries eventually change. You've either got repetitive glue code or abstractions over the library. Both of which reduce quality. Declarative programming is probably the answer but I wonder if that just punts the quality problem further down the stack.
- sdevonoes 5y agoPerhaps related, but after ~10 years or so of experience working as a software developer I tend to value the following: - I care much more about modularity and clear boundaries between systems than about code quality within individual systems I know modularity and code quality may be related, but most of the time we can clearly distinguish them. I don't care much about variable naming, abstract classes vs interfaces, functional programming patterns, SOLID, DDD... as long as we are talking about a system (or module) that is contained and has well defined boundaries to the outside world (or to other systems/modules). Yeah, I know, DDD should touch on the topic about clear boundaries and the like, but in my experience people use DDD as an excuse to introduce CQRS, event buses, a huge myriad of directory layers... The typical example is the monolith that is developed early on in a startup: it has no boundaries, no clear scope, but every engineer cares deeply about "hey, you should not use inheritance there! Let's use interfaces instead", "uh, that variable name doesn't look right", "oh, that's not restful enough", etc., ... but everybody ignores the elephant in the room that is going to cost the company a fortune (if they survive long enough): no modularity at all. Give me any day of the week N modular systems with clear interfaces (but written poorly inside) over 1 monolith that has no clear boundaries but follows whatever is newest in Fowler's blog.
- bunandcheese 5y agoSOLID = [S]ingle Responsibility Principle, [O]pen/closed Principle, [L]iskov Substitution Principle, [I]nterface Segregation Principle, [D]ependency Inversion Principle DDD = Domain-driven design CQRS = Command and Query Responsibility Segregation
- dgb23 5y agoI feel like Fowler typically communicates the "it depends on context, and you need to think on your own" part well. If you haven't read it: I think you'd like the book A Philosophy of Software Design by Ousterhout. It has a very strong notion of what you said and discusses this in a down to earth kind of manner. Very few terms were invented in that book, but it showcases a collection of sensible heuristics and actionable advice.
- joshmn 5y agoI love this article. I’m a firm believer in the strengths of well-written code, even in the age of “move fast and break things.” One of the points this touches on is idiomatic code. In front-end development, there are lots of resources for organizing and styling components for cohesion across a website. For some reason, there’s rarely a consideration to do this for the back-end, at least in the dozens of applications I’ve worked on and contributed to over the past few years. The last few larger teams I’ve been on (15-50 developers), I’ve had the responsibility for putting together best practices (or at least better practices) and style guides for the back-end. While I love doing it, I wish they were a forethought rather than an afterthought; only done when shit has already started hitting the fan because of situations that look like this article alludes to. Developers at these organizations have regularly reached out to me periods of time later showing appreciation for putting them together - so I must have done something right. Even developers that were hired after I no longer was with the organization. You could argue that the back-end is more complex; it is. But for the majority of operations in your very typical application (glorified CRUD on some flavor of an MVC framework), there’s always a dozen ways to get the same result. Asking your framework to do things that it doesn’t explicitly address is where the hairiness comes in. Things like how to build this complex query based on four different contexts? How to represent a form from an object that isn’t backed by a row in the table? How to handle business logic in a way that doesn’t just involve copying a controller method into an object and invoking it, and then copying that object into another object when I need to do the same thing in a slightly different context somewhere else? How to format this string in a way that won’t require me to make changes to the seventy-two different places we currently have it when it’s not always in the view-layer? How to architect a feature that lives by itself in the monolith? Answers to these questions may be easier to find here on HN, but my typical colleague isn’t on HN (for better or for worse :)). Just like the typical web application is mostly CRUD. “Best” or even “optimal” solutions to these questions is often a matter of opinion, but in regards to extensibility, maintainability, and legibility, there’s usually some symbiosis. Such agreement is something I keep front-of-mind now. Does that mean I’ll forego putting together these guides? No, I enjoy writing them because I enjoy having them. I love Breath of the Wild’s choose-your-own-adventure format, but less so when I’m pumping out code.
- chexx 5y ago
- ken47 5y agoWhile not every developer can agree on what good code is, I find it much easier to agree on what bad code is. Bad code increases the risk of bugs, slows maintenance and updates, and is bad for the bottom line. It may be hard to sell non-technical managers on writing the best code ever, but at the very least, sell them on avoiding bad code.
- carapace 5y agoWhat are some examples of high-quality code? I find Maxime Chevalier-Boisvert's Turing Drawings to a be a great example: https://github.com/maximecb/Turing-Drawings https://github.com/maximecb/Turing-Drawings
- Mikeb85 5y agoThere's definitely a minimum level of code quality required so you don't want to kill yourself, but many programmers go way overboard trying to come up with 'beautiful' solutions to simple problems. I've seen 10kloc files with magic numbers and globals everywhere, it's a nightmare. But I've also seen codebases where literally everything is encapsulated and most classes don't really do anything. There's a happy medium, and I'll take 'code that does things' over beautiful code that doesn't do anything. At the end of the day, code that does things is what makes money.
- pkolaczk 5y agoCode that has too many indirection levels for no business purpose is bad, not good. Encapsulation is only good if it hides the things that the user of the code doesn't need to know. Loose coupling is not quality by itself - it can be either good or bad, depending on the context.