87

I feel that I am good at writing code in bits and pieces, but my designs really suck. The question is, how do I improve my designs - and in turn become a better designer?

I think schools and colleges do a good job of teaching people how to become good at mathematical problem solving, but let's admit the fact that most applications created at school are generally around 1000 - 2000 lines long, which means that it is mostly an academic exercise which doesn't reflect the complexity of real world software - on the order of a few hundred thousand to millions of lines of code.

This is where I believe that even projects like topcoder / project euler also won't be of much help, they might sharpen your mathematical problem solving ability - but you might become an academic programmer; someone who is more interested in the nice, clean stuff, who is utterly un-interested in the day to day mundane and hairy stuff that most application programmers deal with.

So my question is how do I improve my design skills? That is, the ability to design small/medium scale applications that will go into a few thousand of lines of code? How can I learn design skills that will help me build a better html editor kit, or some graphics program like gimp?

2

9 Answers 9

89

The only way to become really good at something is to try, fail spectacularly, try again, fail again a little less than before, and over time develop the experience to recognize what causes your failures so that you can manage potential failure situations later on. This is as true of learning to play a musical instrument, drive a car, or earn some serious PWN-age in your favourite first person shooter, as it is of learning any aspect of software development.

There are no real shortcuts, but there are things you can do to avoid having problems get out of hand while you are gaining experience.

  • Identify a good mentor. There is nothing better than being able to talk about your issues with someone who has already paid their dues. Guidance is a great way to help fast-track learning.
  • Read, read some more, practice what you've been reading, and repeat for the entire lifetime of your career. I've been doing this stuff for more than 20 years, and I still get a kick out learning something new every day. Learn not just about up front design, but also emergent design, testing, best practices, processes and methodologies. All have varying degrees of impact on how your designs will emerge, take shape, and more importantly, how they last over time.
  • Find time to tinker. Either get involved with a skunkwork project through your workplace, or practice on your own time. This goes hand-in-hand with your reading, by putting your new knowledge into practice, and seeing how such things will work. This is also the stuff that makes for a good discussion with your mentor.
  • Get involved with something technical outside of your workplace. This could be a project, or a forum. Something that will allow you to test out your theories and ideas outside of your immediate circle of peers in order to maintain a fresh perspective on things.
  • Be patient. Recognize that earning experience takes time, and learn to accept that you need to back off for a while in order to learn why and where you have failed.
  • Keep a diary or a blog of your tasks, your thoughts, your failures and your successes. This isn't strictly necessary, however I have found that it can be of great benefit to you to see how you have developed over time, how your skills have grown and your thoughts have changed. I come back to my own journals every few months and look at the stuff I wrote 4-5 years ago. It's a real eye-opener discovering just how much I'd learned in that time. It's also a reminder that I got things wrong from time to time. It's a healthy reminder that helps me improve.
5
  • 45
    +1 for try and fail. Because when you don't understand why there is a design pattern, you can't use that pattern effectively. May 24, 2012 at 6:12
  • 2
    +1 great answer, but I find that it's somewhat incomplete. I believe that the most important contribution by far would be to have a very good appetite for refactoring. Write, look at what the theory says (articles, books or mentor), refactor/rewrite, go back to theory, refactor/rewrite - this will give you time to focus on structure, while working with familiar code. Be your own worst critic. I would also say that it's very important that you never ever ever lose this appetite for constantly revisiting your own work.
    – vski
    May 24, 2012 at 9:06
  • 1
    @vski There are many concepts I could have included, but the question is whether those concepts would of themselves provide a reasonable path to earning the experience needed for the OP to consider his/herself an improved designer. In the scope of my answer, I see refactoring as a practice (as per my second point). So too is practicing Clean Code, Test First, BDD, and many other concepts. I've taken the approach that there are many skills and experiences needed in order to develop to a point where design skills will emerge and grow, with experience and knowledge earned, over time. :)
    – S.Robins
    May 24, 2012 at 10:09
  • 2
    +1 for getting a mentor. Ideally, get your mentor to do code reviews with you. Having someone else read and criticize your code can really help you when it comes to better and cleaner design.
    – Leo
    May 24, 2012 at 14:29
  • 2
    "Ever tried. Ever failed. No matter. Try Again. Fail again. Fail better." --- Samuel Beckett.
    – Peter K.
    Apr 12, 2013 at 21:13
16

Well, there's no golden apple for this kind of question, and I feel perhaps this is for every coder himself to find what's right for him. Here's my take, anyway.

You could read books on the subject. Great books. Fantastic books. But I find that these books only help you once you've tried to build and design an application - and failed.

For me, it's all about experience. When I started as a rookie I read books on how to design. I didn't understand much of the content back then. When I started working and had to design applications myself, I made very messy applications. They worked, but they were a pain to maintain. Then I read those books again - and this time I better understood them.

Now, I continue making new mistakes and learning from the old ones.

1
  • 10
    There's a great point here that's well worth calling out: Keep making new mistakes; don't keep making the same old mistakes - learn from them, and do something new.
    – Bevan
    May 24, 2012 at 6:02
11

Stop designing and learn to refactor code. Incremental development with continuous and aggressive refactoring will result in a much cleaner end product than any up-front design.

6
  • 3
    Emergent design is a beautiful thing IMHO, yet without discipline you risk creating "emergent spaghetti". The problem with up front design is that people see it as an all or nothing proposition, giving it a bad rep when things go bad. This reminds me about one of Joel's articles where he mentions that design is important. What is needed though is enough up front for a design to make a difference without it robbing you of time, resources, and of your chance to see beautiful supporting designs emerge organically through clean code.
    – S.Robins
    May 28, 2012 at 14:15
  • @S.Robins: I felt the OP is still attacking projects small enough to be completed very well with TDD and continuous refactoring. He can thus learn the discipline needed to know how much design is needed for more complex projects. May 28, 2012 at 20:31
  • I thought that might be the case, however I felt it might be worth adding a counter to the implication that "any up-front design" is going to be potentially worse that something that is purely emergent. I do agree however that the way to build the necessary experience the OP is seeking is to worry less in the beginning about design and focus instead on writing well factored and clean code. :-)
    – S.Robins
    May 28, 2012 at 23:59
  • 1
    I do not agree that refactoring always leads to the best design. Of course, refactoring often allows you to explore and understand the problem, but good design does not always emerge incrementally through refactoring. Sometimes you see a much better solution and realize that your current code is so far away from it that rewriting is much faster than refactoring.
    – Giorgio
    Apr 12, 2013 at 20:55
  • 1
    I had this experience recently: I kept refactoring and refactoring and going in a circle having the same problems again and again: I was using iterators to code something and the code kept being way to complex. Then I decided to forget about iterators and I had to rewrite much of the code, but the logic became much clearer and more concise than before. I do not know if you would call this "aggressive refactoring": the overall structure of the application was not changed but some fundamental parts were just thrown away and rewritten from scratch.
    – Giorgio
    Apr 12, 2013 at 21:00
7

Read about patterns, sure, but first and foremost read about anti-patterns. Recognizing anti-patterns is important, and it's easier to understand why something shouldn't be done in such a way than why it should.

See http://sourcemaking.com/antipatterns/software-development-antipatterns for example.

Write code so that it can be adjusted quickly if requirements changed (which is very common in production environment).

Be super-skeptical about adding "just one more little hack." One more here, one more there, and the code becomes unmaintanable.

Value the open/closed principle.

Write tests (as in TDD). They force you to think your design through even before you actually implement it.

Browse the code of open source projects (reasonably sized ones, that is). I used to be surprised at - usually - seeing so many levels of abstraction. Now I understand it's not art for art's sake, there's a reason why it's done this way.

4

One principle that I find very important for good design is decomposition: if a class is too big (more than, say, 300-400 lines of code) break it up into smaller classes; if a method is too big (say, more than 50 lines of code) decompose it; if a project contains more than 50 classes, decompose it.

The key is to estimate the size of your system and construct several abstraction layers (e.g. subsystem, application, project, module, class, method) that allow you to decompose your code into understandable units with clear relationships between them and few dependencies.

5
  • 1
    While there's some merit in your suggestion, lines of code don't really matter, behaviour does. If your method is doing more than one thing, it's probably time to refactor it. If that one thing is just calling methods that do one thing themselves, and stitching those together, that's fine.
    – jer
    Apr 13, 2013 at 14:36
  • 1
    @jer: It is a rule of thumb, of course. A method calling 100 other methods is, as you say, just stitching things together (it is calling a list of sub-functions). I am more thinking about methods that contain some real logic: it is normally a bad sign if you have to scroll a lot back and forth to understand what a method is doing. Same thing if you look at a class definition with lots of members (and the class is not just a big flat collection of properties).
    – Giorgio
    Apr 13, 2013 at 14:50
  • "project contains more than 50 classes, decompose it" isn't serious
    – dynamic
    Apr 13, 2013 at 23:58
  • @yes123: It was meant to give an idea. It really depends on what you are developing, in what language, etc.
    – Giorgio
    Apr 14, 2013 at 0:28
  • I think it would be sensible to note that this won't help for up-front design (since you're refactoring) but will help to learn patterns and best practices which will improve your future designs. Apr 29, 2013 at 14:54
0

It's tough, what we're really talking about is an ability to abstract rather than create better code, but two things will make you better and one thing will make you happier:

"Better"

A) Find the best designer you can and pair program / do a design together. Ask them to explain what they're thinking as they address the problem, don't settle for"it just feels right" and keep digging. That process will help the "mentoring" party too

B) Imagine everything as individual actors and conversations between them. Each of the actors should have a single role / responsibility and groups of them handle different systems. If that conversation works and each actor feels coherent and cohesive then you're on your way.

And "Happier"

C) If you've tried your best and it still isn't happening then there's nothing wrong with accepting that some people can't do some stuff. You could write tight, brilliant code, but never be able to design or architect. So what? I can't play physical sports for toffee, I'm not good looking and my car driving will never be better than average. Revel and utilise what you're good at.

0
-1

In my personal experience read others code is a good source of "inspiration". I mean try to understand other people designs and ask yourself why he/she do things in that way ?

you can find a lot of open-source projects for research.

anyway you need practice.

-1

Don't live in fear

Strive for simplicity

Listen to your users

Try lots of ideas

Create something, then make it better

Work on things that add value, abandon things that don't

-1

Learn to ask the right questions. More often than not you'll improve your design by looking at the problem from a different angle. In particular this will help you to move away from focusing on solving the problem at hand and look more into solutions that solve multiple related problems.

Not the answer you're looking for? Browse other questions tagged or ask your own question.