Awesome list. While I know I'm not the only one who also does these things, it's good to see these sorts of things brought out in the public from this perspective. Yes, we're all human, and we all know we can do better. Thanks for being honest in public.
I spend so many days wondering if I'm a terrible programmer after looking at code I wrote only days before. I've slowly succumbed to the belief that it has more to do with daily personal growth than the iniquities of my past self.
I need to start seeing things that way. It's really hard, though, when I'm reading The Daily WTF and I go "that doesn't look so bad. I've written code similar to that".
At least I can look at ideas I had 6 months ago and say now that that was a really stupid idea. Progress...
For you to have drawn this conclusion here, it seems you must have decided that either Tommy Morgan or mgkimsal (or both), specifically, are worse programmers than you. ;)
indeed, it might very well be me, although I've seen enough other stuff to make me think I'm not the worst.
One of my fave WTFs was seeing a user profile table with 180 columns in it, one for each country - "is_us", "is_uk", "is_france", etc - with a y or n (or null) in each, and 180 hand-done queries in the login page to figure out what countries you lived in. I've never even come close to writing something that bad, but mostly because I'm lazy. Had that code even been in a loop, I'd have been less harsh on it.
That's crazy. One of the weirdest things I've seen was a column named POrN with a datatype of bit(SQL Server's equavalent of bool). We had no clue what it meant. We guessed maybe some lonely developer stuck that in there one evening long ago.
Eventually, we discovered that it was actually P or N which stood for "positive or negative". It still didn't make much sense and needed to be renamed, but we got a good laugh out of it. I guess the original developer just didn't realize the association. The code was from India, so I'm sure English was not his first language.
And to be honest, all of those things have their place too. If time is money, then code is hard currency. There are no hard rules about how to address code - it depends largely on the context - who's waiting, what timely opportunities are pressuring you, even your intuition about how code Should be strutured can be important, more important the the reason you opened the source file.
Point number 4, "I regularly succumb to the pressure of my todo list, and let myself push quick fixes out the door when I feel overwhelmed." isn't necessarily bad though.
While I'm not the biggest fan of todo lists, well planned and well organized lists do actually help me get things done in a clear and focused manner. If a deadline for a project is looming over my head and I have a handy, well planned todo list for said project handy, I am going to be pushing lots of quick fixes that let me cross things off of my list.
That being said, perhaps this only works well for me because I do take some time prior to writing/adding to the list and attempt to think before I make a list and as such, things turn out better.
I don't think he was saying that todo lists were bad per se. I think he was saying that he implemented short-term fixes instead of more elegant solutions that would cause less problems in the long run, and that this was a result of the pressure of having lots of things to do, whether in list form or otherwise.
I love todo lists as well. Keeps me organised and focussed :)
I understand, but if things need to get done, fixes need to get pushed out instead of wasting time working on more elegant solutions. Isn't that why one plans and ideates beforehand? If the work appears to be overwhelming, you can at least fall back on an organized list so if you do have to cut some corners, the structure of the project doesn't get damaged too much.
Ah, I see your point. One might argue that working on more elegant solutions saves time in the long run rather than wastes it, but that's a different debate it seems.
I think your last point is key; taking time to think about it before adding something to the list. I'm the same when a deadline is approaching, I like to have a list of everything outstanding to meet the deadline so I can work through it without worrying I've missed something.
The flip side for me is the long term todo lists which end up being more like bucket lists of things which 'ought to be done at some point'. I now have four buckets in Trello, 'bucket list', 'next 6 months', 'this month' and 'this week' for general ToDo's (as well as separate boards for individual projects) which mitigates lists like this to an extent as when I'm actually trying to get things done I can just look at the 'this week' bucket.
Long term todo lists wouldn't work with me. In about a week I end up doing much more than what would have been on my long term list, and that boils down to the list never getting done and me getting frustrated at the process. But we definitely have something similar to your Trello bucket for 'future enhancements' or 'quick-wins' that we'd like to complete within the next 3-4 iterations, or 2-3 months, etc.
Daily todo lists have always been a value to me, but I rarely have to actually write them down because of work order tickets or bugfixes semantically being the same thing (to me).
For none software tasks I keep looking for a way to make, for want of a better word, 'exponential' todo lists. So if something needs to be done in the next twelve months, it remains very high level in the to do list "arrange trip to America for example", and as the deadline gets closer, it becomes more granular.
I've had some success doing this with the sub ToDo lists within Trello cards so as it moves from 12 months to 6 months to 1 month to this week it gets broken down into smaller pieces but it still doesn't feel quite right.
Can definitely relate to this, particularly when I'm working on feature x but it looks like feature y is almost certainly going to be required at some point.
It just seems so much easier to include the ground work for feature y while writing feature x. Then by the time it's clear how much extra time it will take to debug and test those bits of groundwork, it's too late to return to a focussed implementation of feature x.
I don't have problem with debugging and testing - since I'm programming in Ada most of the things that compile are already working properly (modulo errors in logic of program, but nothing in reality can help with this). In fact the only place where I just need to do serious testing is in UI and input reading (who knows with what someone may try to feed your program).
But this demands some designing before programming - and then you are seeing what additional features would be nice to have... and you get my version of feature creep, where you have so many features you "have" to write that you don't have time to release version alpha.
I definitely tend to suffer from the second type as well, lucky enough to have a great product manager who keeps the specs tight and limits how much I can "just add this one really cool thing!"
I sort of agree, but the hairs on the back of my neck stand up with that specific phrasing. "Taking on technical debt" implies that doing these things is always a conscious effort taken in the interest of expediency.
In a lot of cases, stuff like this is really just part of the process of learning to be a better coder, and that's not a process that ever really ends. The attitude that poorly done code must either be intentional or is a sign of a bad coder can be toxic in teams, and I think it's important to realize that even good coders make mistakes, and what seemed like a good idea at the time can show it's warts months from now when the shortcomings of a solution is obvious.
It's delicate - as you fear you could go too far with the line of thinking. So far I haven't regretted my choices, I move fast and I pick patterns that fail gracefully. I know the debt is compounding, and I feel like I know when it's appropriate to pay it back.
I'm paying it back now, actually, as I finalize our public SDK. I don't regret leaving it until now, I would have had unit tests covering stuff we didn't really need for the business.
I like the acknowledgement that we aren't all perfect—not even as close to perfect as we tell ourselves and others we are. It actually reminds me of this collection of Isaac Asimov stories I've been reading called The Edge of Tomorrow, with each essay or story featuring a scientist. Asimov denies a deliberate connecting theme, but what I keep noticing is how scientists are just humans too; they aren't these ultra-intelligent, purely rational subspecies. Similarly, programmers may have an understanding of computers that others lack, but we are just as prone to imperfections, mistakes, and inefficiencies even though we work in our own domain of expertise. Acknowledging that seems more useful and honest (to me) than claiming that a certain methodology will make you capable of coding the perfect program the first time around or something.
Conclusion feels like meaningless fluff. There are people who genuinely don't realise that test first is better; it's worth sharing the fact that it is. "Doing it wrong" is just a buzzphrase at this point.
Let's say I am writing the front end of a web app.
I have no idea what its going to look like.
What I do is just get something on the screen, figure out what's looks "wrong", get it fixed and show it on the screen, rinse and repeat until it looks "right." Then I write tests so that future updates will have something to test against.
How would a "test first" paradigm apply to this? If I don't even know what I'm making, what tests do I write?
You figure out what it should look like (which shouldn't involve writing any code - use html or a dedicated UI mockup tool), then write tests for that, then write code for it. If you start by writing code you'll end up with the UI that was easiest to code rather than the UI that looks how the end user will want it to.
Semantically (and probably officially) that's not really 'test first'. You wrote something else (html) then wrote a test for that. You should be writing a test that checks for the existence of that specific html, then watching it fail, then writing the HTML.
If you think that's wrong, then 'test first' isn't always the 'right way'.
No, I'm not being facetious or sarcastic. Just pointing out that 'test first' isn't a requirement. It's an approach that has some good points. I 've done portions of some projects 'test first' and they generally turn, but it's not an approach I use exclusively 100% of the time.
"If you start by writing code you'll end up with the UI that was easiest to code rather than the UI that looks how the end user will want it to."
The problems with this thinking (which I mostly agree with, btw) is that not every project gets to involve checking with end users, nor do end users really understand what/how something should work until they're using something that actually allows a degree of interactivity. They may not even be aware of what's possible until they're poking around 'live'. While you might be reluctant to rejigger large portions of what you've done to move toward "easiest to code vs what they want/need", that's a different problem. It's a problem you need to be aware of, and ready to tackle when you cross it (and you'll know when because you're now aware of it).
The HTML you wrote isn't part of the live code, it's part of the test. It's perfectly normal to write some sort of skeleton/dummy as part of writing your tests - it's impossible to write a test without an interface to call into, after all.
" it's impossible to write a test without an interface to call into, after all"
This is not at all what pure 'test first' has even been described as to me. You'd write a test - run it to watch it fail - then write the code to make the test pass. Whether that code is interface/ui/logic/whatever shouldn't make any difference.
"In test-driven development, each new feature begins with writing a test. This test must inevitably fail because it is written before the feature has been implemented."
EDIT:
"Test-driven development is difficult to use in situations where full functional tests are required to determine success or failure. Examples of these are user interfaces... "
The TDD article on wikipedia is seeming to confirm that TDD for UI is hard (or is at least a shortcoming of a TDD approach).
I like to apply a few different methods to developing web applications:
I write unit tests to ensure the correct data comes out. This usually involves doing a quick "get a blank page loading" bit of boilerplate, then putting the tests (and required data) in place. This involves a fair bit of discussion, sitting down with a calculator, and drawing on the whiteboard. Then I write the code to pass the tests.
In parallel, the screens are being designed and put up on our wiki. Once this is done, I'll make the screen look like the mock-up, which involves a visual "test".
For more complex stuff, this is done in a more piecemeal fashion, although the design is usually put in place in one go. What I'm working on right now has involved 4 iterations of this so far.
For what it's worth, writing the tests first forces you to make your code easier to test which, for a web app, is incredibly useful. I've seen some web applications developed without testing in mind, which tend to have the effect of "funnelling" the user through specific steps. They tend to get a little fragile when the user inevitably steps outside the narrow corridor they're pushed down.
This list is inevitable I think for anyone who is trying to ship or has some sort of deadline. The most important thing I find in maintaining large code bases is to vigilantly keep the broken window theory in mind (http://en.wikipedia.org/wiki/Broken_windows_theory). It takes some discipline to actually go back and fix things that are broken, but it does help to avoid longer term software rot.
No codebase is perfect at any single given point in time, but when you see a broken window, fix it.
Here's something I see with well informed beginners: They have to do everything perfect all the time.
No you don't.
Technical debt is like actual debt: It's bad to let it pile up, but it can give you powerful leverage. Don't quite know how to do something? Well, if you don't even know exactly what it is, it's going to be much harder to write it in an elegant way from the get-go! Write it so it works at all, then refactor your way out of stupid architecture.
I did this the other day, even though I already had an architecture designed, and what I came up with was even better than what I had thought of. The code will talk to you. There may well be CodeSmells. By playing with code, you can often see EmergentDesign.
Technical debt isn't a sin. It's a tool, but it's a tool that can bite you, so use it wisely! (Analogy: It's the shop owner who should decide when to get a loan to buy inventory, Not The Customer!)
38 comments
[ 3.3 ms ] story [ 94.0 ms ] threadAt least I can look at ideas I had 6 months ago and say now that that was a really stupid idea. Progress...
One of my fave WTFs was seeing a user profile table with 180 columns in it, one for each country - "is_us", "is_uk", "is_france", etc - with a y or n (or null) in each, and 180 hand-done queries in the login page to figure out what countries you lived in. I've never even come close to writing something that bad, but mostly because I'm lazy. Had that code even been in a loop, I'd have been less harsh on it.
Eventually, we discovered that it was actually P or N which stood for "positive or negative". It still didn't make much sense and needed to be renamed, but we got a good laugh out of it. I guess the original developer just didn't realize the association. The code was from India, so I'm sure English was not his first language.
While I'm not the biggest fan of todo lists, well planned and well organized lists do actually help me get things done in a clear and focused manner. If a deadline for a project is looming over my head and I have a handy, well planned todo list for said project handy, I am going to be pushing lots of quick fixes that let me cross things off of my list.
That being said, perhaps this only works well for me because I do take some time prior to writing/adding to the list and attempt to think before I make a list and as such, things turn out better.
I love todo lists as well. Keeps me organised and focussed :)
Elegant solutions tend to have to be part of a phase two initiative to be refactored at a later date.
The flip side for me is the long term todo lists which end up being more like bucket lists of things which 'ought to be done at some point'. I now have four buckets in Trello, 'bucket list', 'next 6 months', 'this month' and 'this week' for general ToDo's (as well as separate boards for individual projects) which mitigates lists like this to an extent as when I'm actually trying to get things done I can just look at the 'this week' bucket.
Daily todo lists have always been a value to me, but I rarely have to actually write them down because of work order tickets or bugfixes semantically being the same thing (to me).
I've had some success doing this with the sub ToDo lists within Trello cards so as it moves from 12 months to 6 months to 1 month to this week it gets broken down into smaller pieces but it still doesn't feel quite right.
All of life is trade-offs, no?
It just seems so much easier to include the ground work for feature y while writing feature x. Then by the time it's clear how much extra time it will take to debug and test those bits of groundwork, it's too late to return to a focussed implementation of feature x.
But this demands some designing before programming - and then you are seeing what additional features would be nice to have... and you get my version of feature creep, where you have so many features you "have" to write that you don't have time to release version alpha.
In a lot of cases, stuff like this is really just part of the process of learning to be a better coder, and that's not a process that ever really ends. The attitude that poorly done code must either be intentional or is a sign of a bad coder can be toxic in teams, and I think it's important to realize that even good coders make mistakes, and what seemed like a good idea at the time can show it's warts months from now when the shortcomings of a solution is obvious.
I'm paying it back now, actually, as I finalize our public SDK. I don't regret leaving it until now, I would have had unit tests covering stuff we didn't really need for the business.
There's a social factor to being a professional too; it's not just raw programming skill or experience.
I have no idea what its going to look like.
What I do is just get something on the screen, figure out what's looks "wrong", get it fixed and show it on the screen, rinse and repeat until it looks "right." Then I write tests so that future updates will have something to test against.
How would a "test first" paradigm apply to this? If I don't even know what I'm making, what tests do I write?
If you think that's wrong, then 'test first' isn't always the 'right way'.
No, I'm not being facetious or sarcastic. Just pointing out that 'test first' isn't a requirement. It's an approach that has some good points. I 've done portions of some projects 'test first' and they generally turn, but it's not an approach I use exclusively 100% of the time.
"If you start by writing code you'll end up with the UI that was easiest to code rather than the UI that looks how the end user will want it to."
The problems with this thinking (which I mostly agree with, btw) is that not every project gets to involve checking with end users, nor do end users really understand what/how something should work until they're using something that actually allows a degree of interactivity. They may not even be aware of what's possible until they're poking around 'live'. While you might be reluctant to rejigger large portions of what you've done to move toward "easiest to code vs what they want/need", that's a different problem. It's a problem you need to be aware of, and ready to tackle when you cross it (and you'll know when because you're now aware of it).
This is not at all what pure 'test first' has even been described as to me. You'd write a test - run it to watch it fail - then write the code to make the test pass. Whether that code is interface/ui/logic/whatever shouldn't make any difference.
http://en.wikipedia.org/wiki/Test-driven_development#Test-dr...
"In test-driven development, each new feature begins with writing a test. This test must inevitably fail because it is written before the feature has been implemented."
EDIT:
"Test-driven development is difficult to use in situations where full functional tests are required to determine success or failure. Examples of these are user interfaces... "
The TDD article on wikipedia is seeming to confirm that TDD for UI is hard (or is at least a shortcoming of a TDD approach).
I write unit tests to ensure the correct data comes out. This usually involves doing a quick "get a blank page loading" bit of boilerplate, then putting the tests (and required data) in place. This involves a fair bit of discussion, sitting down with a calculator, and drawing on the whiteboard. Then I write the code to pass the tests.
In parallel, the screens are being designed and put up on our wiki. Once this is done, I'll make the screen look like the mock-up, which involves a visual "test".
For more complex stuff, this is done in a more piecemeal fashion, although the design is usually put in place in one go. What I'm working on right now has involved 4 iterations of this so far.
For what it's worth, writing the tests first forces you to make your code easier to test which, for a web app, is incredibly useful. I've seen some web applications developed without testing in mind, which tend to have the effect of "funnelling" the user through specific steps. They tend to get a little fragile when the user inevitably steps outside the narrow corridor they're pushed down.
No codebase is perfect at any single given point in time, but when you see a broken window, fix it.
http://pragprog.com/the-pragmatic-programmer/extracts/softwa...
No you don't.
Technical debt is like actual debt: It's bad to let it pile up, but it can give you powerful leverage. Don't quite know how to do something? Well, if you don't even know exactly what it is, it's going to be much harder to write it in an elegant way from the get-go! Write it so it works at all, then refactor your way out of stupid architecture.
I did this the other day, even though I already had an architecture designed, and what I came up with was even better than what I had thought of. The code will talk to you. There may well be CodeSmells. By playing with code, you can often see EmergentDesign.
http://c2.com/cgi/wiki?CodeSmell
http://c2.com/cgi/wiki?EmergentDesign
Technical debt isn't a sin. It's a tool, but it's a tool that can bite you, so use it wisely! (Analogy: It's the shop owner who should decide when to get a loan to buy inventory, Not The Customer!)