Change the Wrong Way

Many factors can force software to change, such as new business requirements or new technology requirements. As software developers, we have to handle the constant stream of new demands in a maintainable way. But sometimes we don't have the time, or we don't have the resources. We just need to make it work because they need it like yesterday! When we allow ourselves to get caught up in the frenzy, we let quality slip. Other times, we just didn't know. Or we had bad practices that we thought were fine. Quality slips all the same. Can we see these issues just by looking at the code?

public List getPersons(String name, String firstName, int contacts, boolean sort, boolean dir) { ResultSet rs; List persons = new ArrayList(); if (name != null) { rs = db.query("SELECT * FROM People"); addAllPersons(rs, persons); if (sort) Collections.sort(persons); if (dir) return persons; } else { rs = db.query("SELECT * FROM People WHERE last…

Frameworks of Failure

As programmers, we are always seeking to match great problems to elegant solutions. We want to solve a problem in a way that solves it forever. The larger the problem is, the greater our need to find that permanent solution. This thinking is what leads us to create hulking pieces of software that try to solve every problem but solve no problem well. These frameworks we create aim to be general enough to apply to an entire category, but lack the concrete details to be applicable to any specific need.

We build these frameworks because building them is more interesting than the actual problem we have to solve. Who wants to write the software to track auto parts in a warehouse when we could write a framework that maps relational data to object workflows? We could use that framework on any problem like that. We just need to add tens of thousands more lines of code tightly coupled to our framework. Too bad all we end up writing is a giant framework and a giant business logic layer to... tra…

The 7 Worst Atrocities of Cleverness

We know we shouldn't be clever, but what is clever? There are seven particularly bad forms of cleverness, from very small to very large.

Bitwise Logic and Ternary Statements
This is the lowest-level cleverness. We write a one-liner that uses bitwise logic or a ternary statement. They start out innocently. We have an assignment based on a simple condition. A full-blown if-else block feels like overkill. Then that turns into this:

int i = 16 + (!(a & 4 == 0 ? thing : other).getValue().equals(b & 1 == 0 ? first : last) ^ (b & 2 != 0 ? w[0][1] : w[0][0])) ? counter - 1 : counter << 1; That's not clever. That's a train wreck. Singletons and Lying About DependenciesDesign patterns are general solutions to common problems, and are often useful. But Singleton is one "pattern" (actually an anti-pattern) that can be destructive to a codebase. Singletons are easy to write and easy to use, but they are just another form of global state. Programmers who…

Don't Be Clever

I was recently watching a talk by Dan North. At one point he briefly talks about a project he worked on. For this project, the Ant build script was generated with an XSLT transform, producing a build script about 2 million lines long. Even though that is clearly horrible, my first fleeting thought was, Huh, I would have never thought to do that. That's pretty clever..

Dan North's Simplicity, the Way of the Unusual Architect, at the time of the story

That's pretty clever, is absolutely the wrong thought to have. I cannot fathom the monumental, wasted effort maintaining such a system. But I am also sure it started as someone's clever idea. There would be plenty out there who would say, Well, that project went wrong, but the idea itself isn't bad. It's pretty darn clever, really.

"Cleverness" like that always seems promising at first. It is a novel view of a problem at hand. Because clever ideas are novel in completely unexpected ways, they have unexpected…

Stand Up Against Stand Ups

It's finally happening: your team is finally going Agile, whatever that means. The business has complained for too long. The team has complained for too long. You need to move faster and do more. Word comes down that you're going to try Scrum. This shouldn't be too bad. After all, Scrum can't be that hard to master. Well, turns out that there's a lot to Scrum, and your team can't just switch over in one day! Let's take this slowly, adding in the simple parts and going from there. Timeboxed iterations make sense. Your team already has a weekly meeting, so sprints are a week. That was pretty easy. What's next? Product owner, no; retrospectives, no; story points, no. Aha, stand-ups! Once every day, you stand up and talk about stuff. That's easy enough. Done. You are so Scrummy now!

Or not so much. That all-too-common story of Agile Transformation highlights a number of pitfalls that derail real change. The most deceptive practice, though, is the daily …

Exposing Dependencies: Getting Worse Before Getting Better

As software developers, our job is managing dependencies. We have to carefully construct balanced graphs of classes, objects, libraries, services, and more. Code needs to be encapsulated, reusable, and extensible, all while creating end-user value. We use techniques like composition, inheritance, and dependency injection to separate this from that, knowing that this or that could change in unknown ways at any moment.

We don't always get to create the perfect system from the ground up though. Sometimes, we work on software developed long ago that could not anticipate today's needs. It hurts a little more when we see our name on all those commit messages. Regardless, we need to make the necessary adjustments for our system to meet the needs of today and tomorrow. To make code more flexible and adaptable, we often have to break it up into more singular and discrete responsibilities. We have to pull out and encapsulate the pieces that are at different levels of abstraction. We hav…

Inheritance as Polymorphism vs Inheritance as Code Sharing

We've all heard the mantra that we should favor composition over inheritance, and in most cases this is good advice. Occasionally, though, we really do need an inheritance relationship. Inheritance provides that useful “is-a” relationship that sometimes fits a situation so well. The pitfall we have to watch out for is using inheritance for code sharing between classes that should otherwise not be related, and not for polymorphism, that “is-a” relationship.

What makes code sharing through inheritance so bad in the first place? An inheritance relationship is one of the tightest dependencies that could be made between two classes. It is hard-compiled in, and cannot be swapped out at runtime. It's an all-or-nothing relationship, and anything the parent wants to expose, the child must also expose. The child class may be able to override some of the parent behaviors, but the parent may finalize some methods and prevent this, locking the child class in even more tightly. This may be …