Professional corner-cutting
blog.ometer.com
blog.ometer.com
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'.
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.
> 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.
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.
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.
Relevant: http://media.gettyimages.com/photos/cooking-oil-containers-a...
The original post is just giving us a metaphor - you need to decide how it applies.
This is what I thought of when the artice said "visible toolmarks". An abstraction layer missing/not fully implementer exposing "ugly" plumbing code.
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.
There is almost never enough time or money to do every single thing perfectly.
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.
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.
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.
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.