Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Professional corner-cutting (ometer.com)
92 points by rfreytag on May 9, 2016 | hide | past | favorite | 29 comments


While I understand the point being made here, it is worth remembering that cabinets are not continuously modified by large teams for years on end.

Corner-cutting generally makes things harder to maintain, which has a severe knock-on effect in software, less so for furniture. As a result, I think there are often fewer corners that can be safely cut in software. So, in many scenarios, this advice essentially boils down to the somewhat self-evident 'don't do completely pointless things'.


Cabinets might not be continuously modified for years, but the house as a whole will be. The backs of the cabinets will continue to matter much less than the fronts, both for aesthetics and maintainability.

On the other hand, somebody's definitely going to regret that hacky thing you did inside the wall when you remodeled the kitchen and knew it was a bad idea at the time but did it anyway.

Admittedly, though, I may just be too emotionally invested in this analogy thanks to my house's previous owner's infuriating obsession with construction adhesive.


The people who invented construction adhesive, expanding foam sealant, and that stupid rubber-in-a-can sealer need to all be drug out into the street and shot.


That foam sealant is a crime against construction. It's always hideous, and usually far more long term trouble than a proper solution would be.


I literally facepalmed when I got to "construction adhesive". You have my heartfelt sympathy.


At least in a private industry context - tis is the #1 reason how developers often justify over-engineering. Actually, vast majority of software either never ships or fails to achieve any meaningful use / market leadership (i.e. no one buys it), because the engineering team was too busy future-proofing it. The truth is, your software is most likely to become irrelevant lightning fast (remember the 90s?), and you should consider yourself lucky to incur maintenance debt :)


Cutting corners in order to move fast means that later on, when you're dealing with competitors and upstarts trying to take your place, you can no longer move fast enough because of all the technical debt you incurred early on.


The solution then is as the article points out:

> An apprentice painstakingly smoothing an invisible surface would be yelled at for wasting time. An apprentice failing to smooth a visible surface would be yelled at for producing crappy work. To become a professional, the apprentice learned to work efficiently but still do a good job.


yes but... if you don't deliver, there won't be no future for product. anyway, every single product out there got major overhaul/complete rewrite every few years/versions.

I think this often boils down to key people's personality traits - to have everything possible under control. can work on smaller parts/projects where it delivers well, but once applied at large scale can fail spectacularly.

I wish I could say what that magical silver bullet is, but it's specific per case - a fine balance of effort/real value, with just enough resilience to not fail too fast. or maybe not.


> every single product out there got major overhaul/complete rewrite every few years/versions

That's just wishful thinking. Most products do not go through a complete rewrite. They may change dramatically over time, but that's typically due to incremental changes, not a from-scratch rewrite.

For context, I'm working on a project right now that has a reasonable amount of technical debt incurred at least 4 years ago. We're eventually going to have to rewrite big chunks of the app specifically because those parts aren't very maintainable. If they had been written correctly the first time we wouldn't have this issue. And in this case, it shouldn't have been hard to write it correctly; doing things right does not necessarily mean doing them slower.


The issue is, people rarely build the right product on the first try. Your reasoning here assumes that the thing you've spent time building well was the right thing to build. It's worth getting things out quickly initially to find out if anybody wants it. You don't want to compromise the experience of your product (external). Thus it's best to sacrifice features and reduce complexity, in order to test ideas and the quality of product experience. Writing code is just a means to this end.


You're talking about something completely different here. Putting out an MVP is not the same thing as writing sloppy code that becomes a maintenance burden later.


Many startups do this, and the "later on, when you're dealing with competitors" doesn't matter - by then you have money and market share.


I'd throw "the perfect is the enemy of the good enough" on as well.


OK, I'll start: When I write code, I spend a fair amount of time on making sure it's readable (e.g. StyleCop compliant on the first pass, well-commented, human friendly abstractions) and it's not for aesthetics. It's so the next poor bastard who has to change it doesn't have to stay up all night inferring my intentions from a bunch of one-letter variables. There's usually no "back of the cabinet" where I can safely cut corners.

Relevant: http://media.gettyimages.com/photos/cooking-oil-containers-a...


And don't forget that "the next poor bastard" might be yourself, 6 months later, after having forgotten all the details about the code.


The place to cut might be somewhere else (like leaving out unnecessary features or abstractions).

The original post is just giving us a metaphor - you need to decide how it applies.


"unnecessary ... abstractions"

This is what I thought of when the artice said "visible toolmarks". An abstraction layer missing/not fully implementer exposing "ugly" plumbing code.


As a maintainer, I both adore and prefer one-letter variable names when appropriate - loop indices for old crufty array index oriented languages, temporary variables that all references can be seen on one page, that sort of thing.


Cool, you sound amazing. I'm tasked with maintaining an old part of the app now and would appreciate hearing how you feel this way


It's just Stockholm syndrome.

a one-letter variable name does communicate "this variable is of no consequence, it works like an index register works in assembly, and you should be able to prove it correct by inspection. Wish it had a "foreach"... "

plus, I read a lot of signals code - filters, tranforms, where the one-letter names are translations from formulae. It's "you can write Fortran in any language" code.

Celebrate the diversity.


You're going cut corners either way, either because you made intelligent choices where to cut corners, or because you ended up being forced to do a shitty job on whatever you were working on when time/money runs out.

There is almost never enough time or money to do every single thing perfectly.


Here's an alternative: don't make time a factor while you build a foundation, then, when time does become a factor, simply extend the foundation as far and as rickety as necessary knowing at the very least you have a rock solid foundation.

Practical example:

You're building a CRUD app. Start by implementing robust logging, monitoring, testing, containerization (if prudent), and a proven framework-environment, then create all your endpoints and your client application.

When your app inevitably fails the logging, monitoring, testing, etc, will be a nice consolation prize.


The counterpoint here is that it's oftentimes hard or impossible to know what exactly you'll eventually be building before you build it, especially with something like a startup where experimentation until you get product-market fit is the norm, or a project which has an expected life of multiple years/decade(s).

If you sit there pre-planning every last detail of something, that usually means you end up making a lot of assumptions that eventually turn out to be false. It can be a sad day if one day realize you that your entire code is structured towards doing stuff one way, and then your requirements are the opposite. This is true even when you think you've thought of every possible option - life has a way of being surprising.

Specifically abstraction is a tool to be used strategically, not a blunt instrument to bash your code with. Rushing into abstraction can kill a good codebase because it's difficult to refactor code to extricate yourself from a bad abstraction. Also a lot of abstractions come at a cost, e.g. the build takes longer, there is more boilerplate to get a simple thing done, you need multiple servers and orchestration to run the app, etc etc

I'm finding that the thing to do instead is to build things open to change and extension, but initially allow some inefficiency / repetition / stupidity in favor of less complexity. You build in complexity as you need it. Examples: Don't start with the fanciest logging stuff, do use a uniform function call that is easy to find / replace later on. Don't start with containers or even separate repositories, but do keep logic between areas of code cleanly separated such that you can pull them apart easily later, etc.


To add: You can pretty much create re-usable templates of robust logging, monitoring, testing, etc. It makes things way simpler and cuts down development time. For example, I follow the same principles and "finished" a Django-based application in about twelve days (with a good amount of tests included). No corners cut. Just leveraged previous code/experience.


Just my own singular data point but I've found that time spent getting the foundation right is never wasted. By the middle of the project, that early investment in engineering has usually paid for itself many times over in terms of faster feature implementation, higher reliability, and less time spent debugging and head scratching. Not to mention the quality of life improvement that then knocks on into better attitudes and motivation.


I believe you are close to the concept of reversible decisions. Those base tools you discuss are all things you can't just tack on later on when they suddenly become important.


Some food for thought there.

This I quite liked - probably because I've seen it happen a few times:

> We should not ask customers for more precision than they can give us (a symptom of this is to badger customers or managers for detailed “requirements,” then complain endlessly about “changing requirements”).

I think the idea of context is vital really. If you are a hired gun building something you aren't personally going to use, then you do need to be able to put yourself in the customer/user's shoes. Ideally, you should get to know the subject area well enough to be your own customer so you can bridge the gap between what the customer says they want and what they actually need but just haven't expressed yet.


It is hard to accurately compare software to any other craft because changes are requested practically forever. (Nobody asks a woodworker to go back and add a 4th drawer to a dresser that was built 6 months ago.)

I find that highly-granular revision control systems go a long way toward avoiding cruft and they remove the temptation to cut corners. When you are halfway through a huge change and you encounter something that is causing problems, carve out the problem into a separate commit and fix it! Similarly, if something that was expected to be a simple feature turned out to require general architectural changes, carve out the general changes and commit them first.




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

Search: