Today’s code smell is feature envy. Feature envy is when one class uses the methods of another class to excess. It’s called feature envy because classes that use methods from another class excessively look envious of that class’s features.
The problem with that, as usual, is the way your logic gets spread around between multiple classes. If you have one method that’s using another class so much that it’s hardly even separate from it, you’ll probably have to change things in both places when you need to fix a bug or add a new feature, but the compiler can’t tell you that you need to do that so you risk creating a shiny new bug by missing a spot.
Feature envy probably makes more sense with an example. Let’s say you have an address class and a customer class, and when you call customer.getMailingLabel() your customer class calls address.getStreetNumber(), address.getStreetName(), address.getCity() and so on to build a complete address string for printing a mailing label. Let’s imagine that policy changed and you’re now able to ship to rural mailboxes, so you need to add route and box numbers to your address and to use those instead of street number and street name if they’re present. If your code has feature envy, you have to change both the address object and the getMailingLabel method to support rural mailboxes. If you moved the getMailingLabel method into the address class, then you would only have to change one class when you wanted to change how you build mailing labels.
As with just about all code smells, feature envy isn’t always a sign there’s a problem. Sometimes you want to separate your data from operations on it, especially if you’re using a strategy or visitor design pattern. If, for example, you want addresses formatted differently for different reports, the formatting is probably more the job of the reporting code than the address object. In that case it would make more sense for a visitor to contain the formatting code so the address object doesn’t have to care about all the different reports.
Jessica Kerr has a really interesting blog post about how code reuse is often overrated. Back in college I learned all about how important code reuse was and how duplicated code was the worst, but we never (probably because we had a lot of material to cover in a limited time) talked about how code reuse can cause problems. Jessica gives a great explanation of how bad code reuse can paint you into a corner, you should check it out.
Today’s code smell is switch statements. Long switch statements or complicated if statements often (but not always!) mean that something has gone wrong with your design. Objected oriented languages give you a lot of tools to avoid big complicated switches, if you aren’t using one of those there needs to be a good reason for it. If you’re not using an object oriented language, on the other hand, I’m honestly not sure if switch statements are a sign of a potential issue. If any of my readers could weigh in on that, that would be really helpful.
But why are switch statements a problem? Because you’re spreading around logic that should probably be in one place, and if you add a value to one switch statement you’re likely to need to hunt down all the other ones and change them too.
If, for example, you have a switch statement that does different calculations based on the type of the object passed in, that logic for how to do the calculation probably belongs inside the object passed in to the switch. If you change that object, you’ll have to hunt down every place it’s used and make sure nothing else needs to be changed, which is obviously asking for trouble :) If you give each object a doCalculation method and replace the switch statement with a simple call to doCalculation, then all of the logic for that object stays inside the object. With all your logic in one place, you don’t have to search for every thing you need to change and risk missing a spot, and your design just makes more sense when logic for one thing isn’t smeared across multiple objects.
Polymorphism isn’t the only way to fix a switchy smell, if your method switches on a parameter that’s passed in, you may want to change it to separate named methods to more directly do the thing. If you have a setProperty(String name, String value) method, you could replace it with named methods like setType, setModel, setYear, etc. You might end up with a lot of little methods, but it’s still clearer than one setProperty method with a giant switch. If you end up with a truly excessive number of methods, that’s another code smell that points to your object doing too much.
Just because switch statements are sometimes a problem doesn’t mean they always are. Sometimes what you’re doing is so simple that an interface and a set of objects that implement it (or changes to your existing interface and objects) makes your code more complicated rather than less. And sometimes there just isn’t a better way to do it: factory methods and abstract factories rely on switches or a series of ifs to figure out which implementation to return.
Design is one of my favourite parts of programming, but I constantly run into the problem of how to tell whether my design is any good.
Before we go any further, I think we should define what makes a good design is. My definition is that if you can change it in the future without cursing your past self, it must have been good design. You may have noticed that using that definition makes it difficult to figure out whether a design is any good until potentially months after the fact when you need to change it. It would be nice to know ahead of time whether your design is going to lead to a lot of swearing in the future, and that’s where I have trouble.
To be fair, if I could definitively answer the question of whether a design was good without having to try to change it, I’d be a millionaire and I’d be writing this post from my own private island :) Whether a design that seems good now is going to continue to be helpful in the future is just a hard problem and there’s no getting around that.
That said, I think code smells can help a lot there. A code smell is, in the words of Martin Fowler, a surface indication that usually corresponds to a deeper problem in the system. The term was coined by Kent Beck while he helped Martin Fowler with his book Refactoring.
This whole concept would probably make more sense with an example. An especially designy code smell is a class that does too much. Ideally a class should have one responsibility – if it’s concerned with more than one thing that’s a strong sign it should be broken up into separate classes. The specific problem with a class that does too much is that it’s likely to lead to long, messy methods that do too many things and don’t make sense because of it, and it generally makes it hard to reason about your system. The more one class does, the more state it’s likely to need to keep track of, and the more state one class keeps track of, the more chances you have to completely mess it up. You may have gotten a hint here that software design is about accepting that humans are bad at it and trying to work around that basic fact :p
The thing with a code smell is that it’s not a hard and fast rule, it’s just a hint that something could be wrong. Sometimes it can be reasonable for a class to do “too much,” it depends on the exact thing you’re trying to accomplish and the context (i.e. the rest of your application) that your class is part of. Having a good design is more about the thing you’re trying to accomplish than it is about following all the rules whether or not they make sense in your particular situation.
The great thing about code smells and design patterns and all that is that they let you take advantage of other people’s experience. Can you imagine how long it would take to get good at software design if you had to make all the mistakes yourself? I love hearing what other people have messed up so I can maybe avoid that particular problem. And from the other side, I’d love it if anything I’ve told another dev helped them avoid a problem. It’s all well and good if I solve a problem for myself, but if I can help a couple of people avoid the problem in the first place and if those people help a couple other people avoid that problem, then I’ve done much more good than if I quietly fixed my own code and moved on.
Stay tuned for more posts about code smells, there are plenty of them to get through :)
Lately I’ve been working with Ember components, which are really pretty cool. For anyone who doesn’t know, in Ember components let you encapsulate layout and functionality neatly together so you can reuse things all over your app without making a huge mess.
Ember components have a lifecycle you should know about and a set of lifecycle hooks you can call. Those hooks are really handy, they let you do all kinds of things at different points in the component’s lifecycle. Be warned, though: you MUST call
at the beginning of your lifecycle hook or your component will break in deeply weird ways. When you implement a lifecycle hook in your code, you’re actually overriding a method in the base Ember component. If you don’t call this.super(), the original function doesn’t get called and shockingly enough, it does a bunch of things that are necessary to make your component work right :)
Because Ember is open source, you can go have a look at what the base lifecycle functions do. Here’s the init function, which calls this.super() itself before it does the rest of its setup. Reading the code yourself totally isn’t necessary, but it’s interesting and might help you remember why it’s so important to call this.super().
You might have noticed I like watching conference talks and recommending my favourites, and you might have gotten the wrong idea about my attention span from that :) I actually have a terrible time concentrating on watching a video if that’s all I’m doing, my mind wanders and when I come back to the video I have no idea what’s going on so I have to skip it back to catch what I missed. After that happens a few times in a row I give up and go do something else.
Fortunately, I have a trick to keep myself focused. It’s kind of a ridiculous trick but hey, maybe it’ll work for you. What I do is open up the talk I want to watch in one window, and in another window I open up a silly clicking game like Diggy’s Adventure or Clicker Heroes and play that while I listen to the talk. The reason having those open in separate windows is important is so you can alt-tab to the talk easily if you want to see the current slide.
For me games like those are the digital equivalent of doodling, they give me just enough to do to stay focused but not so much that I get distracted from the talk I’m trying to listen to. They’re also great for convincing myself that I’m really just playing games with a talk on in the background, which makes it not feel like work so I actually do it.
If you want to watch more recorded talks but never seem to get around to it or your mind wanders when you do get around to it, try a really low impact game. It might help and definitely can’t make it worse :)
Technical debt is one of those things that you completely understand when you run into it, but is really hard to explain to someone who has never worked on a large application. That picture on the left of the messy server room is probably the best illustration of technical debt I’ve ever seen, and you should definitely use it the next time you need to explain technical debt to someone.
You can guess exactly how it happened, can’t you? “I know this is the wrong way to do it but we’ve got to get this server back up NOW so just string a cable over to that other switch and we’ll tidy it up later.” FYI, that never happens just once. “Later” when it’s time to tidy it up never comes either. There’s always something more important than going back and fixing something that technically works.
Even when you have the time to make a change the right way, it’s a huge hassle because the server room is already such a mess. Eventually, it gets so bad it’s just impossible to change anything without first tidying the whole thing up. That’s what technical debt is like in code, it’s just harder to see.
Every time you cut corners to get something out the door quickly, you build up technical debt. You make a little bit more of a mess, and as the mess gets worse and worse, it gets harder and harder to make more changes, just like in the messy server room.
Just like financial debt, sometimes it’s worth taking on technical debt. And just like financial debt, you need to think carefully about it before you take on that debt and you need to have a plan to pay it off. Ignoring the fact that you have technical debt is like ignoring the fact that you’ve maxed out your credit card, it just gets worse if you don’t deal with it. Unlike financial debt you don’t directly get charged interest on technical debt, but every piece of new development you do has to work around your existing debt. If your project is dead and you don’t have to change anything then your technical debt doesn’t matter, but on a project that’s still being updated you’ll eventually have to work around the corners you cut. Every time you do that without tidying up the original mess, you make more and more of a mess until you end up with something like that server room above.
All of this is completely obvious if you’re a developer who has worked on a large project, but technical debt can be really hard to explain to non-technical people because they aren’t programmers. That photo is a fantastic visual metaphor for technical debt, which is why I think it’s so great. Once you have a metaphor like that, you can have a productive conversation about technical debt with a non-programmer because they now have the slightest idea what you’re talking about :)
It would be great if management would just trust the dev team when they say they need time to pay down technical debt instead of continuously pumping out new features, and it’s definitely an issue if different parts of the company don’t trust each other, but even when they do I think it’s reasonable for managers to want to understand what’s going on. As a programmer I certainly understand the urge to yell and stomp your feet about people who don’t understand technical debt (or why sometimes things take way longer than you expected or why adding people to a late project makes it later or why you need to make time for documentation, etc, etc), but you know, it’s a lot more productive to find a way to explain it.
I have mixed feelings about job postings that require a certain number of years of experience in any particular language. If you understand programming concepts (from low level stuff to how ifs and loops work to higher level stuff like how to keep your core business logic separate from presentation code) then yes, it’s pretty easy to apply those in whatever programming language you need to.
As a bit of an aside I think a lot of job postings are more of a wildest dreams wishlist than a useful description of what’s actually necessary to do the job from day to day. In the context of this particular question, I have a strong suspicion that when most job postings say the applicant needs “5 years of experience with Java” what they actually need is someone who knows Java and has 2-5 years of programming experience.
That said, while most programming concepts are transferable (especially if you’re using languages with broadly similar syntax), you really are more productive with a language you already know well, especially if you don’t have much programming experience. There are weird quirks of languages you may not run into at all until you’ve been working with them for a while, too.
For example, did you know in Java String.substring() used to return a “view” of the original string, not a completely separate string? And that since Java 1.7 update 6 it returns a completely separate string? Yes, that seems really minor but it can cause some weird weird bugs if you change the original string thinking it’s completely separate from the substring. And because stuff like that doesn’t look wrong, it can be absolutely miserable to track down.
So given all of that, why do (some) employers make such a big deal about language specific positions? Because if you don’t already understand programming really well, you need some way to find people who have a decent chance of succeeding in the position you want to fill. Sadly, not all job postings are written by people who understand the day to day work so it’s not unusual to end end up with some poor HR person’s best guess at what’s needed.
“5 years of Java experience” can also be a pretty decent proxy for “actually likes working with Java and won’t ditch us in a year to work in [cool new language of the week]”. Some languages are just not considered cool and plenty of people have a perfectly legitimate dislike of the amount of boilerplate you have to write to make Java do much of anything, so it makes sense to look for people who are willing to work with your tech stack for the long term (or at least the longer than one year term).
Readers, what do you think of stuff like “must have 5 years of Java experience” in job postings?
The first thing you’ve got to do is accept you will never get perfect requirements. If anyone knew how to get perfect requirements every time no matter how large the feature was, you would know their name because they would be deservedly famous for finally fixing the requirements problem. Outside of school assignments, the requirements you get are practically always going to be incomplete in some way and probably wrong in at least one way too. It’s frustrating, I’m not going to lie, but that’s just how the job works.
I’m stressing that point so much because I want you to have realistic expectations about how much you can possibly improve your requirements gathering process. If you go in thinking you can ever get perfect requirements every time, you’re just going to be disappointed. On the other hand, if you have more realistic expectations about what kind of improvements you can make, you’ll end up a lot happier with your progress.
Now that I’ve gotten the disclaimer part of this post out of the way, there are some things you can do when you get incomplete requirements or when you work with people who often change their minds about what they want.
First of all ask questions! I touched on this briefly in my post about how communication can make you a better programmer, but it bears repeating. Communicating well is absolutely essential to being a professional programmer, and one of the many reasons it’s so important is that you need to make sure you completely understand the requirements before you build something. Don’t worry about looking stupid, ask questions about anything that you’re not sure you completely understand. Unless you work in a terrible toxic company, nobody is going to be mad that you cared enough about doing a good job to make sure you didn’t accidentally build the wrong thing.
Asking questions can get you quite a ways and is definitely worth doing, but it’s not always enough. Another really useful technique is mockups. When people can see an example and compare it to what they already have, they’ll understand it far better and will be able to give you much better direction about what they really need. It’s a lot of work for the human brain to have to imagine what the finished thing will look like, how it will work, and how you will use it all at the same time. Take a little cognitive load off of your requirements people by asking them to imagine fewer things and you’ll get better results.
There are tons of tools out there for mocking up UI, if nothing else you can throw together a quick slide deck or build a couple simple pages in HTML. Don’t forget to take a lot of notes when you walk your requirements people through your mockup, you’re working with a limited human brain too and you’re not going to remember every detail of the feedback they give you without notes.
After you’ve made some mockups and walked people through them, you may want to build a prototype before building the real feature. Once people can actually use something, it becomes real to people in a way written requirements just aren’t. It’s not because they’re dumb or bad at requirements, that’s just how humans work. Building a prototype will also bring up all the technicaly questions you didn’t realize weren’t answered in the requirements until you started building the feature. Don’t pretend you’re perfect at requirements either :)
There’s another really useful technique for gathering requirements, but unlike questions, mockups, and prototypes, it takes more buy-in from your requirements person or people. That technique is user stories (you may encounter some confusion about use cases and user stories, which seem to be similar but not quite the same thing. I just found out when I was looking for a good link to explain further that the things we call use cases at work, other people call user stories).
To very briefly define user stories, they’re a particular way of stating requirements. The general form is: as a_____ I want to _____ so I can_____. For example: as a blogger, I want to schedule posts for the future so my posts go up on a consistent schedule. It’s amazing what comes up when you think about what you users want to accomplish and why. That kind of context is really important to understand the intent of the feature, but it does take more work on the part of the person who comes up with your requirements and writes them down.
It’s pretty common for people to see all this question asking and mockup making and user story writing as a waste of time that just slows down development. If you’re willing to put in the effort, it can really help to record just how much development time gets wasted because the requirements are bad or get changed all the time. Developer time is really expensive, showing your manager exactly how much developer time gets wasted on building the wrong thing then throwing it out when you get better requirements will likely convince them that it’s worth putting a few more hours into getting better requirements.
Requirements are fundamentally a hard problem you can do everything right and still get a horrible surprise when you’re halfway through building a feature. Like everything else in development, there’s no silver bullet.