Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I've had occasion to think a lot about the relationship of quality to long term productivity. The remarkable thing about building software quickly is that if you focus on speed, cutting quality where needed to "move forward", you end up slowing down after a while, due to the build-up of technical debt. A continued focus on speed will typically result in slower development, and a low-quality product, a sort of worst of both worlds scenario.

On the other hand, if you focus on quality, you end up making choices that enable fast development. You make sure you have high-quality requirements and a deep business domain insight in the whole team, which means you end up writing only that code which you truly need, and write it in the right order (most important features first). You make sure you have code that is well-covered by tests, which as a happy side-effect makes it easy to adapt to new requirements. You focus on having a high-quality deployment process, so the amount of customer issues that result from a broken deployment go down reducing the load on your dev and support teams.

Focusing on quality first is not about gold-plating code, it's about making conscious decisions to have a high-quality process from front to back, and those decisions typically result in a highly efficient organization. It's no accident that those teams which manage to ship releases every few weeks (e.g. firefox, chrome, facebook) have world-class products. You have to focus on quality to go fast.

This isn't just theory. Capers Jones actually measured this. The fastest delivery schedules were produced by teams with the highest quality product. http://www.parasoft.com/products/article.jsp?articleId=2769



I agree with this. I think sometimes it's hard to keep the balance between the developers who want to 'do things right' but spend far too much time designing a perfect solution, as opposed to many coders who rush to 'get something done' and build systems which quickly become impossible to maintain.

I think after about 5-6 years of coding was when I came to the realization that everything you build 'the best way' will become awful coding practice in a few short years. But that writing things at blazing speed just to hack it together and get to work invariably leads to code which is written to work a single way and is a nightmare to maintain.

I try to build small, flexible solutions which are as adaptable and maintainable as I can, with the awareness that whatever I build will be looked as as archaic in a few years, but it might still need to be maintained and upgraded.

So, slow down a little, think a little, code a little slower, but don't spend 6 months planning the perfect code weapon either.


> everything you build 'the best way' will become awful coding practice in a few short years

Disagree.

Good architecture is almost timeless. It isn't always en vogue, but it is recognizable by being pleasant to maintain, conceptually cohesive, and simple. It usually resembles a domain model, with the UI, persistence, and everything else being built around it.

It is a force at least as strong as the language used.


> Good architecture is almost timeless.

As long as nothing changes, I can agree with you. But that's the point of architecture in the first place, to be there for you when things change. The focus of architects should be to ensure that changes are as easy as possible and not to have an elegant design. I've seen some of the most elegant designs that are easy to maintain trashed due to changes in user requirements or changes to the market place.

For this reason I advocate, and have advocated since I started developing in the early 90s, micro service/modular architectures. Java OSGi is currently my favorite environment (though I'm also investigating similar approaches in Clojure), but I've done this with C/C++ in the past as well using shared objects/dlls as the modularity mechanism.

Good architectures adapt to change, they doesn't stay the same with time.


Elegance doesn't matter; simplicity does in the face of changing requirements. Or rather, elegance falls out of this simplicity. It's an emergent property, rather than an explicit aim. Thus, my favorite designs have traditionally resembled a microkernel in approach, where the kernel of the app exists only to facilitate disparate services to find and use one another. Interfaces are kept to an absolute minimum, and concerns like UIs or persistence are pushed to the edge of the system, where they should be. This design facilitates division of labor well; more novice developers can make a mess within their subsystem, yet have the damage contained. I usually bolt a pub/sub mechanism on relatively quickly, as this requirement is almost a given with a UI.

Dynamic loading is indeed the ultimate modularity. Combined with this microkernel approach, extensions are just as powerful as built-in services. The only problem is you enter a new hell: managing dependencies/versions of the core vs the extensions. If you can control the release of the extensions as well as the core, you're set.


> Good architecture is almost timeless.

Never dismiss the dangers of over-engineering. While not as terrifying as under-engineering, how useful is a feature that exists only for the purity of design?


Over-engineering is a problem because it usually results in a much more complicated design than you really need and wastes a lot of time.

However, over-engineering and spending quality time thinking about the design and domain are two separate things and typically the more you think about the design and domain the less likely you are to over-engineer.


And Over-Engineering is something people only learn to spot once they've designed and built a few systems and had to maintain them for a few years. Its fairly easy to teach design-principle-du-jour but quite hard to teach about over engineering, KISS and YAGNI.

Developers tend to want to generalise everything because it makes it more interesting and has the appearance of good design. But generalising in one direction reduces flexibility in other directions. Better to copy and paste a bit at first and wait a bit before adding that extra extraction layer that generalises Foo.


I think a lot of it comes down to developers focusing on their own personal learning and development and not what is best for the current project.

Very few workplaces will let you work hard for 5 hours solving problems in the most time effective way and then spend the next 3 hours on your personal development.

On the other hand you can always spread the work over 8 hours and "experiment" with "on the job learning" leaving all sorts of sub-optimal solutions in the project. Then the next guy goes "Why on earth did Jim use X here?" and the truthful answer is because Jim wanted to learn more about X.


Yeah, I've heard several devs say something like 'Why did Jim do this...oh, crap, he was geeking out on this new thing.'


What we're looking for in design is simpler. Simpler becomes conceptually harder very quickly, because it means challenging a lot of assumptions and opening up some unknowns.

The tried-and-true implementation has a known initial cost and cost scaling factor as it's repeated throughout an application, while an experimental (and overdesigned) one is extremely sensitive to context and could become a net negative if it fails.

When we talk about "technical debt," I think we're describing something that isn't an experimental design, it's just "below" the known standard - it's faster to implement, but it cannot scale, and we already know that. Conversely, a standard practice is one that works and doesn't usually get pushed to its tolerance limits.

The corollary of this is that for any engineering project taking on a big enough problem, you have to assume some technical debt to ground the system in a place where each module can be tested as part of a whole: otherwise your work will go too deep into the experimental/overdesigned zone before it ever completely functions.


I see lots of worry about over-engineering on the Internet, and little in practice. I'm sure it happens, but under-engineering is way more common.


Yeah, in my experience, "no engineering" is the most abundant; everything is thrown together willy-nilly to code as fast as possible. I have come across very few real cases of over engineering.


Part of my point is that you don't feel over-engineering nearly as much. If it is under-engineered, it probably doesn't work, and at least has a list of pain points that are fairly obvious.

Over-engineered components will work (sometimes flawlessly), so you don't realize you've spent too much time and money on something until long after you built it.


Over-engineering ISN'T good architecture. It's just as much bad architecture as under engineering.

;)


Yes, never dismiss it. In fact a lot of the time over-engineering is just another form of technical debt, but it also has an upfront cost as well. At least the under engineered solution is cheap up front.

I would much rather come across something under engineered than over engineered.


So, at one time, relational databases were considered 'new'. Entire programs and the data they used were stored in a single file by most development teams.

It was considered good architecture to keep everything together. Many established and well-respected developers argued against relational data, because they said 'it splinters all of the data' and modular programming 'splits up the application logic all over God-knows-where'.

Some people still argue if Linux and it's monolithic kernel is better or worse than Windows modular design. These are two completely different architectural schools, and yet both have people who consider one good design, and the other flawed beyond repair. It doesn't matter which, take your pick, they both have zealots and detractors.

So, saying 'good architecture is timeless' is kind of general for me.

Things you consider to be absolute today will change and be defunct in five years.


I think consistency plays a big role here.

If you're into small modules, stay consistent and separate things. If you're into OO-design, try to encapsulate and make your objects re-usable.

When you start losing this consistency and drift away from the architectural vision in order to make quick-wins ("lets just tuck this feature to this module even though it might not really belong there..."), that's when things turn into mud.

Understanding the architecture and its philosophy gives you a big insight into the code, the way its structured, where things fit together. It saves you time.

With solid architecture you know where to change things and where to add them. I think it's timeless in this sense. Its consistency applies everywhere. Once you 'get it', and stick to it, things make sense.


This has been my experience as well. There is only one speed, and that is doing things well.

I'm convinced a lot of developers have been completely brainwashed by the Business in believing they don't need to ship good code. They absolutely do, and the cost of it is high. The cost of maintaining bad code is much, much higher, however. Abdicating this responsibility reflects poorly on you, the developer.

Much of the populism in software targets the insecurity of those who don't push themselves to ship great code: "getting things right is so hard! Just do the best you can and don't sweat those edge cases too much!" I sweat the edge cases when I'm writing the code in the first place because I'm incredibly lazy: I know the mental cost of re-creating all the context necessary to solve the problem later is very high, and I'd rather not return to the feature ever again.


"A week of coding saved me 30 minutes of planning."

That's one of my favorite sayings.

>I sweat the edge cases when I'm writing the code in the first place because I'm incredibly lazy

I like to front-load any problem solving and make sure I understand what I am getting myself into. What that looks like in practice is that, when designing architecture, I will write up a list of every scenario I can think of (within reason of course, also including future plans), and verbally test each scenario against my ideas. Essentially I try to build up the largest image I can of all the moving parts, and then narrow the scope back down to the present problem. Initially expanding the scope to include edge cases and potentially fictitious scenarios not only helps me isolate concerns, but I consequently understand the limitations of my implementation (which of those scenarios failed my verbal testing) and I have my notes for when/if I return. It's how I sate my natural desire to be overly-comprehensive without actually over-engineering everything; I am confident that what I build today is flexible and extensible enough to grow as it needs to. Sometimes I open a file I haven't opened in ages and I read a comment saying, "You're welcome to my future self", and I can't help but be thankful.


There is a lot of truth to what you're saying, but while it sounds great for software that is built for its own sake, it sounds too uncompromising in most business settings. The fact is that most software is built in service to a business and the developer is (quite rightly) judged by the extent to which the software serves the business well, rather than the extent to which the code behind it is "good" or "bad". Ignoring that and attaching your identity to that quality of the code rather than the service to the business is folly. Having said that, it is all a short-term / long-term trade-off; "bad" code can be expedient and beneficial short-term while "good" code is almost always better in the long term. Deciding when to serve short-term and when to serve long-term business goals is the Hard Part, but the answer isn't to stubbornly apply one rule or another.


As someone who works at a place that has tons of technical debt yet is able to release daily, I have to disagree.

We have things that are 6+ months old without any of the normal best practices and are able to continue to ship w/o delay.

Hell, for a new project/service two of my coworkers completely bypassed version control and unit tests.

You can achieve high velocity without high quality. The cost to do that is sometimes you have an expensive production bug to fix.

Please note, I'm not endorsing the way my coworkers do things but I've given up fighting any of it outside of my narrow slice of turf. ;)


This is a giant mess waiting to happen. Eventually the pace of development will slow to a crawl as incoherence of the technical decisions made build up. Then when the developers leave, forcing the next guy to work to understand now-legacy projects, all of a sudden the velocity the company has heretofore enjoyed will be gone.


It has been this way for 10 years and predates me working there. The pace of development remains unchanged as far as I can tell over the time I've worked there, regardless of whom left and was hired.

I was pointing this out not to say it was a good thing but rather that the premise of [Quality == Speed] is flawed.


It sounds like releasing daily is part of your testing strategy in that you simply let your customers do the testing for you.

Making small incremental changes with extensive manual testing can work. It just looks like you don't have to pay for the test effort.

I've seen it a fair bit with internal projects at larger companies but most external customers I have met in my career would quickly switch products.

Edit: It also sounds like you are confusing best practices with good code. I've met teams, usually without a formal cs education, who haven't used version control and didn't have any unit tests, but still had a well designed codebase. Of course best practices are best practices for a reason....


> Edit: It also sounds like you are confusing best practices with good code. I've met teams, usually without a formal cs education, who haven't used version control and didn't have any unit tests, but still had a well designed codebase. Of course best practices are best practices for a reason....

1) They both have degrees in comp sci.

2) For very narrow definitions of the word "good" you would be correct. Its technically functional and reliable. However, it regularly has problems with security and/or changes to the common library breaking other people's services.


Hahahahahahaha. If only. <3

Its an ecommerce site. So its all external customers [including API integrations with external IT departments]. I'm amazed we get away with it tbh.


The critical thing is that the architecture remain coherent. Modules have to have clear responsibilities and not get cluttered with special-case hacks that really belong somewhere else. If you keep things reasonably clean that way, I think you can survive being sloppy about other recommended practices... barely, and with effort, but you can survive.

But if no one can understand how the system works and predict the effects of changes, the thing is doomed.


Yes. I wouldn't say doomed tho. It just leads to production bugs that are costly.


Question: What is on-boarding new employees like with that system? How long does it take you to get people up to speed and being productive? I would suppose the lack of rules makes them fast in the beginning and in the end they are contributing to the mess in their own special way, doubling down on the technical debt.


I can't really answer that question completely accurately. I was a very odd case/hire compared to those both before and after as far as I can tell.

I mostly interface with outside developers for integration purposes and I never really had a normal on boarding process.

I'm just a random developer with no authority/power.

That said, my best guess is:

> 1) What is on-boarding new employees like with that system? They get an intranet project that is relatively harmless and helps them learn the common library -> they get assigned projects on the main site and get progressively larger features to implement. There is no real documentation beyond the actual codebase and asking people questions.

> 2) How long does it take you to get people up to speed and being productive? No clue. I'd guess 2 months based on how quickly I switched from "intranet projects" to integrating Amazon MWS, 3rd parties, etc.

> 3) I would suppose the lack of rules makes them fast in the beginning and in the end they are contributing to the mess in their own special way, doubling down on the technical debt.

More or less, yes. An example would be we now have 3 partial versions of the same intranet application. The super old one, the one I built before handing it off to the new guy, and the new guy's version [because he didn't like my version].


> I've had occasion to think a lot about the relationship of quality to long term productivity. The remarkable thing about building software quickly is that if you focus on speed, cutting quality where needed to "move forward", you end up slowing down after a while, due to the build-up of technical debt. A continued focus on speed will typically result in slower development, and a low-quality product, a sort of worst of both worlds scenario.

We're a typical "launch in 3 months!" start-up that is now 7 years old, and we've were so focused on growth and feature development to meet the market that technical debt just accumulated and accumulated.

Eventually, technical debt began to have a real impact on business feature delivery, so we talked about it with our business team and came to an agreement that 30% of development time was ours to reduce this major risk.

The end result is that over time, as quality improved, our business feature delivery has gotten better and faster - less bugs, and more features per sprint. We're very lucky to have our business team, I think.


Very well said. It's a shame so many project managers (and coders!) don't understand that any sufficiently sized project is a million times better off when investing more time at the beginning to lay a solid foundation. I like that more and more people are beginning to understand nowadays.




Consider applying for YC's Fall 2026 batch! Applications are open till July 27.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: