The problem with this blog post is that it's long been used to justify writing crappy code, when it's actually about writing simple code, and those aren't necessarily the same thing. If your duct-tape go kart falls apart going around a corner and gives you a permanent injury you're gonna wish you spent a bit more time at the starting line wondering what to build it out of.
a hundred times this. Technical debt is painful on all levels. It makes things falling apart more likely, it makes refactoring more painful, and it adds cognitive load.
The argument that you can absorb technical debt only really works if you plan on an aquihire to save yourself, because if not it's very likely you'll have to clean it up (and you'll have paid a decent amount of interest on it).
It's not like good code and code that's easy to write are two ends of a line.
> you're gonna wish you spent a bit more time at the starting line wondering what to build it out of.
Sure, but usually spending a bit more time at the starting line (or rather "much more time" or "all the time in the world, until there's no time left and you still have to use the duct tape version, now in an even bigger hurry") won't help you find the problem spots. Hindsight bias is very well and alive and usually all the "yeah, we should have thought about that before" remarks are just that. You wouldn't have thought about it before, because you need the scars to tell you "Bad idea, don't do it again."
As a general rule, decision made due to regret ... well tend to lead to more regret. That happens to me a lot. What a lot of people don't do after a project is to first evaluate how well the whole thing worked.
How many bug incidents were there ?
How many times did a design decision have to be refactored out ?
How many times did you really have a problem ?
Then, first things first, take out all problems that were obviously not project related. The team moved ? Someone went on holiday ? All of those don't matter.
After that, you'll have problems left. If it's 3 problems, none of which delayed more than 2 weeks/10%, your project went as well as you can reasonably expect. Don't change a thing. If after that you arrive at the conclusion that the project went ~80% as planned, then you've simply done it right. There are no lessons to be drawn from mistakes, because the fixes are likely to screw you up more than the "mistakes".
Even when this is not the case, you should limit fixes. Take the top-3 problems, at the very most, and implement a fix for one of them. Then try to mitigate the second and stay away from everything else.
If you don't you'll have seriously more regrets about your next project a whole than your current project.
Lots of people generally don't do this. They do a project, then switch programming languages. That's a decision you should make once in 5 years (unless you're switching back after a disappointment). People seriously overcompensate. From management to developers. Redesigning an entire organisation because one bad apple leaked something is very likely not justified, and similarly a rewrite is almost never justified.
Since I moved into a lead role (where I still do day to day coding but don't do as much as my reports), a good rule of thumb for me has been that if I can't track down the offending line of code within half an hour based on a stack trace coming in from a significant portion of end users, someone has overarchitected something.
Figuring out when something is too simple is trickier, but it tends to be pretty obvious because a ton of bugs come in that are easy to locate.
Over the past few years, I've decided that as a general rule I prefer engineers that under-architect to those that take it too far, because the damage is far easier to mitigate...
I think that confusing "duct tape", "gaffer tape", and "crazy glue" is a problem in any kind of shop. Maintainable glue code is more like painter's tape or gaffer tape than it is rubber cement, imo.
'Zawinski didn’t do many unit tests. They “sound great in principle. Given a leisurely development pace, that’s certainly the way to go. But when you’re looking at, ‘We’ve got to go from zero to done in six weeks,’ well, I can’t do that unless I cut something out. And what I’m going to cut out is the stuff that’s not absolutely critical. And unit tests are not critical. If there’s no unit test the customer isn’t going to complain about that.”'
I couldn't agree more. That's what I cut out as well when up against a deadline. The problem is, when is one not up against a deadline in the professional world?
Because of a combination of no planning, bad communication, and just plain laziness on the part of product, I got a chance to see what not being up against a deadline in the professional world is like. For a few months, my team wrote beautiful code, did in-depth code reviews, tested everything that could be tested, and dotted all the i's and crossed all the t's. To be honest, the pace fell even more than I expected as other team members who didn't know when to leave well enough alone just kept on making or suggesting unnecessary tweaks and re-factorings. While it was slightly cleaner and better, the code we wrote then was only marginally so compared to the code written in haste at crunch time. If I had to put a number on it, it'd be ten percent better. For two or three times the amount of time and effort put into it. The point is that there is always a balance that needs to be found.
I have seen and worked with the type of code that Joel refers to and it's definitely just as bad as any spaghetti. I have also worked with people who intentionally over-engineer or spend a lot of time on unnecessary minutiae that overly complicates code and the design. These are real problems indeed and they lead to code that looks nice from a bird's eye view and even a short inspection but leads to screaming when you have to work on it. I think both the duct tape engineer and his opposite are both extremes to be avoided most of the time.
"I think both the duct tape engineer and his opposite are both extremes to be avoided most of the time." Very true! Everything in moderation.
My first job in software development was addng features or fixing bugs in Drupal sites built by people who had left the company. These were fantastic engineers, but I pulled my hair out every day and fantasized about unit tests, documentation, or even a clue about what was going to happen next.
Even with unit tests today, I still make mistakes -- sometimes big ones. But I also recognize that for software edifices to survive, you have leave a solid foundation for others (or your future self) to build on. Too much engineering and there's company left to employ you. Too little and the product might ship but it doesn't work.
Multiple inheritance often makes it extremely difficult to tell where my code is coming from.
When you don't use inheritance, you often get to refer to another module by name. E.g.
`my_dependency.fetch_data(123523)`
With multiple inheritance, however, that other module has become your own "self"!
Suddenly you're stuck with:
`self.fetch_data(123523)`.
And if you want to find the source of `fetch_data()`, you basically have to grep / look through all seven of your base classes.
If you're lucky, the definition you were looking for calls `self.flush()`, where `flush` isn't defined on this class. It's actually intended to be defined on another base class, which is imported by the multiple-inherited subclass.
So, you effectively have a "DSL" whose "statements" consist of your choice of base classes. It's impossible to determine what's a legal "program" in this "DSL" until you run the thing and get method-missing errors.
Why should I have to learn a hundred cryptic keyboard shortcuts to get what I consider normal functionality? I have in the past used Perl's command line debugger. Why would I want to do that when I have a far more efficient graphical debugger in Eclipse?
I work with bioninformaticians, and many of them are too lazy to have ever set up an IDE. Most of them won't have had autocomplete until Kate included it. Barely any of them uses a step through debugger. Its like a revelation when they ask me for help and I show them it on my machine.
You appear to be advocating a new:
[ ] cloud-hosted [ ] locally installable [ ] web-based [ ] browser-based [ ] language-agnostic
[ ] language-specific IDE. Your IDE will not succeed. Here is why it will not succeed.
You appear to believe that:
[ ] Syntax highlighting is what makes programming difficult
[ ] Garbage collection is free
[ ] Computers have infinite memory
[ ] Nobody really needs:
[ ] a REPL [ ] debugger support [ ] a local filesystem
[ ] to interact with code not written in your IDE's preferred language
[ ] The entire world speaks 7-bit ASCII
[ ] Scaling up to large software projects will be easy
[ ] Convincing programmers to adopt a new IDE will be easy
[ ] Convincing programmers to adopt a language-specific IDE will be easy
[ ] Programmers love learning new keybindings
[ ] There is only one operating system and it is
[ ] OS X [ ] Windows [ ] Linux [ ] iOS [ ] Android [ ] the DOM
Unfortunately, your IDE (has/lacks):
[ ] vi keybindings
[ ] emacs keybindings
[ ] Syntax highlighting
[ ] User-configurable indentation
[ ] Macros
[ ] Written in JavaScript [ ] Written not in JavaScript
[ ] Written in a scripting language you made up
[ ] which is a Lisp
[ ] A windowing system
[ ] Version control
[ ] Only using git [ ] only using github.com [ ] not using git
[ ] using an RCS of your own devising
[ ] Its own platform-independent look-and-feel
[ ] that was designed by a programmer
[ ] based on yesterday's design fads
[ ] applied inconsistently
[ ] A look and feel specific to one operating system
[ ] that was last widely used in 1989
[ ] and was known to cause seizures
The following philosophical objections apply:
[ ] Programmers should not need to understand CSS to change their font
[ ] The most significant program written using your IDE is itself
[ ] The most significant program written using your IDE isn't even itself
[ ] Graphical programming presumes programmers can draw pictures better
than they can type words
[ ] The implementation is closed-source
[ ] covered by patents [ ] not owned by you
[ ] The DOM is not an application framework
[ ] The name of your IDE makes it impossible to find on Google
[ ] Your IDE assumes JavaScript can be made infinitely fast
[ ] You seem to think static analysis is worthless
Your implementation has the following flaws:
[ ] JavaScript is not faster than C, C++, or Java
[ ] The DOM is not a windowing framework
[ ] It crashes on any file larger than 32k
[ ] You provide no way for users to run the program they are editing
[ ] You require the user to check in code before it can be run
[ ] The IDE crashes if you look at it funny
[ ] You don't seem to understand basic optimization techniques
[ ] You think a single string is an acceptable data type for a text editor
Additionally, your marketing has the following problems:
[ ] Unsupported claims of increased productivity
[ ] Unsupported claims of greater "ease of use"
[ ] Obviously faked screenshots
[ ] No one really believes that your IDE is faster than:
[ ] vi [ ] emacs [ ] Eclipse [ ] Visual Studio [ ] IntelliJ [ ] Notepad
[ ] Rejection of orthodox user interface design without justification
[ ] Rejection of usability principles without justification
[ ] Rejection of established platform conventions without justification
[ ] Rejection of basic user interaction without justification
Taking the wider ecosystem into account, I would like to note that:
[ ] Your example workflow would be one key command in: _______________________
[ ] We already have an IDE in the browser
[ ] We already have an IDE that can be scripted using
[ ] Python [ ] JavaScript [ ] A Lisp [ ] Lua
[ ] You have reinvented vi but worse
[ ] You have reinvented emacs but worse
[ ] You have reinvented TextMate but worse
[ ] You have reinvented Eclipse but worse
[ ] You have reinvented Notepad but worse
[ ] You have reinvented Notebad better, but that's still no justification
[ ] You have reinvented ed but non-ironically
In conclusion, this is what I think of you:
[ ] You have some interesting ideas, but this won't fly.
[ ] This is a bad IDE, and you should feel bad for creating it.
[ ] Programming in this IDE is an adequate punishment for inventing it.
Strictly speaking, this issue is not caused by multiple inheritance but just by inheriting from a lot of classes. It also happens with huge Java class hierarchies, for instance.
At a startup, often duct taping is exactly what you need. It can't be worthless code, but good enough is often what's needed.
You need to build something that will get shipped and work for the next little bit. Then, when you have an avalanche of customers you get to replace the engine in mid-flight. That's the dream.
The scenario where you build hugely scalable infrastructure with squeaky clean code is not the dream.
Right now I'm working on save/load for a game that is overflowing with duct tape code. Yes it shipped and did well. But now I'm paying for all the sins of all the duct tape used by all the coders. I'm not sure I'd change anything in the past but I do reserve the right to bitch about things written that aren't fully correct but close enough... until save/load is considered and then it just doesn't work at all. Grumble grumble grumble...
For all the naysayers, I think you're right to be disappointed that things weren't done better. It'll drive you to become a better engineer and spend a little (but hopefully not too much) time building things when it's your turn. Also, don't grumble too loudly -- you still have a job :-)
Every time you bitch, consider this: you've still got users.
It'd be appropriate to bitch about it if you didn't have users. But, in the end, it doesn't matter how fancy things are: the user is the only thing that matters. Ever.
Until the users want a new feature - save/load, say - and you can't add it without breaking the toaster in the office kitchen. And so the users bugger off to play Candy Crush Massacre or whatever :-)
And they can't rationalize why you're saying it'll take months to implement save/load.
The newest SimCity is a great example of this.
"zomg offline mode should be really easy for you guys to write! Why haven't you yet!?" "well... ...technical debt and design decisions that are at odds with that..."
This is a great thing to grumble about. It's also a great thing to only fix enough to hit your performance targets. But you probably already know that :)
I have started taking "I write bad code", "I don't write unit tests" etc with a pinch of salt. I think in any real world scenario there will be factors that pull the code in one direction or the other. Sometimes the most important thing is to ship, sometimes the most important thing is to ensure that you can keep shipping. It is entirely contextual.
I build and manage an in house database. The data model changes frequently. I have started adding in unit tests in the past, but they are rarely worth the time (in these circumstances). I estimate that creating unit level tests and fixtures would probably treble the time needed for features, and would catch maybe 10% of the bugs that appear.
Uses request a feature. I build it. I show them, ask them to test it and tell me if they have a problem. I get plenty of testing done. Just not in the unit test way.
Now I imagine if I had an online app used by millions of people and I was doing a big upgrade I would have a different attitude towards the way I test.
I like this article. I was good at the crazy multi threaded stuff at uni. Now I find I have a lot to learn about making real life products. 3 Years in and I have to admit attention to detail and simple yet robust solutions are more important than understanding the latest and greatest way of doing things.
Sometimes duct tape is the way to go, especially when you are working on a project with a small-ish team where your 'hacks' can be easily understood by everyone.
This article is very relevant to my life recently. Recently I have been working on a tool to boot a full development environment in VM. It's just a series of shell scripts (managed by Vagrant, so some "shiny", but nothing crazy). Everyone wanted me to use docker and someOtherShinyThing.js etc, but in the end the shell scripts are simple, predictable, and they work really well.
I'm on the other end of it with my current project. The "architect" managed to wire about a dozen third party libraries and frameworks together to create a Frankenstein monster that's quite literally unmaintainable. Every day is a new day of Jenga - touch anything and it all just collapses.
The funny thing is it didn't save time at any point in the project. the learning curve to configure all that was so steep we could have written it ourselves in less time. And sure, with a few dozen scattered lines of code I can add a new feature that's almost exactly what the users are requesting. After three days of perusing stackoverflow for a few days and spending another three days trying to figure out where the new cryptic error message is coming from. "Socket timeout"??? Which socket? For what?
I feel for ya. People often rail against reinventing the wheel, but 36 times out of 37, I'd rather have an in-house created wheel that rolls how we know it should with code we understand than some 3rd party bag of voodoo that supposedly roles in the desired ways, maybe.
I would far rather have something put together with well known, documented, tested third party libraries than some undocumented in house crap that one "superstar" developer knows how to work and no one else.
You think that, but in my experience the "well know, documented, tested third party libraries" try to be all things to all people. So when you just need something to cut your steak with what you get is a Swiss army knife with twenty different blades of which you can identify three without spending a week poring through documentation.
Instead of just looking at fifty lines of code to see what the "undocumented in house crap" is doing, you'll be spending all your time on stackoverflow trying to figure out why these third party libraries don't play well together. "Has anyone had this problem with version 3.3.2 of JLSHE and version 8.7 of Loopdidoodle?" Oh, I think I need 3.3.4. But the developer of my GUI framework says only 3.3.2 is tested and I'm on my own if there are problems. Somebody has a workaround but it only runs on RedHat when I'm running Debian. Should work okay if I recompile it, though... except the kernel version I'm running is too far behind.
And your assumption third party libraries are any better quality than a focused in-house solution is a bit naive.
Finally, a few years down the road the mind share for half your third party libraries and frameworks will have dwindled as the people you were counting on to maintain them found something shinier.
Documented and well known is preferable to most of the in house stuff I have come across. Need a Django developer, then there will be a few out there. A new developer on your own in house framework is always going to need some time to get up to speed.
Sure I may be on Stack Overflow for some answers, but you can guarantee that the in house equivalent won't have any Stack Overflow answers at all.
My personal experience is that open sourced libraries in CPAN / PyPI are almost always of a higher quality than in house stuff.
Maybe it depends on the language. When I go for Python / Perl libraries, I usually get something of a high quality. JavaScript stuff always seems a bit more like you describe.
I would say that the developer of the in house stuff leaving is going to be at least as much of a problem as people abandoning open source projects (which are open source, so you can continue to maintain them yourself).
Yes, then 3 years later I need to deploy something new on same server where your "simple" scripts are working. I my current project I need to work on CentOS 5.8 where I cannot install anything new and performance suck.
On the other hand Docker container would create separate env for application that will be fully independent. If you can pay for Vagrant I/O penalty they go ahead.
The scope of my project isn't what you think it is. The goal is to provision a VM with an Android development environment on it, just for the purposes of getting newbies up-and-running. So nobody will be relying on my scripts in a few years.
This is a great article, but the comments make me feel like I'm twice as old as I actually am.
I think that maybe if we called these programmers "gaffer tape programmers", some of the sense might be clearer to programmers who've come to the field (like me) from a linguistics and theatre background, not a traditional mathematical one.
Currently rewriting every single line of UI code a pile of duct tape programmer spewed out over the span of two years. Writing functional tests so I know when I break the UI. Documenting everything. Hitting all deadlines.
Using multiple inheritance. Lots of It. In fact, no file is over 150 lines long. Composition pattern in full effect as well. I hope it wards off duct tape programmers for life.