views:

1318

answers:

21

I'm currently stuck how I should make designing skills as a programmer better. I've seen over a dozen questions about (algorithmic) programming challenges. Defining and verifying a good design isn't as trivial. You can objectively measure how good an algorithm is, but not the design used to implement that algorithm.

I do see problems with my designs, but there's only so much one can introspect himself. Even though I can usually say what's wrong.. I'm not nearly as sure how I could fix it.

I'm aware of the most common design patterns. I understand principles like loosely coupled, DRY, KISS, etc. on a general level. I always go "yeah, that makes perfect sense", but they don't necessarily make that much sense and/or aren't trivially applicable in a concrete context.

Probably the biggest thing I struggle with is separation of concerns. I usually think in terms of snippets of functionality that do something. I struggle understanding who is responsible for what and how I should make all the functionality "glued" together. Most importantly, I have difficulties seeing the logical and complete design. My intuition says there probably should be A, B and C, but what kind of intermediate objects should there be in-between and which snippet of functionality goes where is problematic for me to perceive.

Lately, I've started doing some testing and it has really helped me cover some of the issues, especially making my designs more loosely coupled. Oh, and it reveals bugs. I can warmly suggest (unit) testing to anyone in a similar spot.

+7  A: 

Yes, unit testing can be a really big help in sanitizing one's design as well. Via unit tests, you not only check what dependencies a specific class/method has, but practically check how easy or difficult it is to use in real life. Often this gives valuable feedback to improve an API or class design.

I think another invaluable source of practical experience is refactoring. It helps you optimize your design in small steps, which can add up to big changes, and sometimes help uncover ideas / patterns one would've never thought of earlier. Recommended readings on this topic are

Péter Török
I would personally add "Working Effectively with Legacy Code" to that list, just because we often inherit code rather than write it from scratch.
Javid Jamae
@Javid, it is an invaluable aid indeed, but it does not specifically help one become a better designer. It focuses more on what to do _before_ you can actually start thinking about improving design. Often this involves refactorings which temporarily make the code look uglier, for the sake of allowing the creation of unit tests.
Péter Török
@Péter - Good point, but it still is a step in the right direction. :)
Javid Jamae
+8  A: 

Everytime you are bitten in the butt with a bad design descision, take some time to see how you would redesign it to make it better. Time may not always allow you actually implement it, but at least do the mental exercise to try.

One good way to improve on this is to look at maintain really good well thought out code.

Another is to look at and maintain really bad and poorly thought out code.

Much of this just comes from experience. Since you are already keeping this stuff in mind, you're already head and shoulders above many programmers just concerned with getting the job done.

Byron Whitlock
Inspirational answer! It's just that figuring out flaw(s) in your design feels like taking a punch in the gut and taking a step back. I know executing the re-iteration/re-factoring loop is a necessity, but for some reason it makes me depressed and unmotivated. Perhaps it's because you get a whole less (instant) gratification (because you already have a working system) from re-implementing your design. Maybe I should work on trying to re-wire the way I think about these things and that flaws in designs is something natural.
randomguy
+2  A: 

Patterns, Refactoring, and Test Driven Design all focus on the small. I think it essential to also pay attention to the overall structure. To use a musical analogy, you can know the details of arpeggios, suspensions, marcato, etc. but a symphony is far more than an aggregation of parts.

In the same way, designs have a flow and the best way I know to become more skilled at that art is to

  1. Look at how other systems are constructed.
  2. Hypothesize about how system X is implemented, imagine alternatives and compare them to your model.

I've seen a lot of system designers and people who just can't formulate a path to a solution; yet have not found a trait that predicts which category a given person falls into. My best guess is that it is a talent that can be cultivated, but that some may lack a certain essential skill required for big picture design.

msw
How do you know when to stop with the "what-ifs"? How do you prevent the possible paralysis by analysis caused by 2.? The more you imagine about alternatives, the more they subtract from your initial model's goodness and the more you start doubting.. and start over again.
randomguy
That question could be applied to any decision that you make in life. Engineering is the practical art of making the most effective use of finite resources; your level of skill is one of those resources. Aiming high is good; delivering an imperfect product is a practical necessity. Determining the satisficing design is what makes engineering a discipline. This response is as generic as your comment but also equally valid.
msw
+13  A: 

To get good at design, you need to write some code from scratch (not always easy in a corporate environment from what I understand) and write bad designs. Until you've written a few god-awful designs, things like object orientation, higher-order functions, design patters, metaprogramming and separation of concerns will seem like useless solutions in search of problems. Looking at other people's bad designs likely won't help because bad designs are, almost by definition hard to understand if you're not the person who wrote it.

After you've made your beginner mistakes and written a few monstrosities that become unmaintainable by the time they hit 1500 lines of code, try to grok the principle of late binding/higher order functions. This is more fundamental than OO and IMHO is the most fundamental principle of high-level design. I never got OO and polymorphism until I thoroughly understood this more basic concept. Think about how higher order functions/late binding could have been applied where you instead used more ad-hoc solutions.

Then and only then should you pick up some design pattern books. I recommend Head First Design Patterns because it gives solid information on the "why", not just the "what". Read it with a focus on how you could have made the designs of the programs you wrote not suck, then go back and refactor/rewrite your old programs into something more maintainable using what you learned.

dsimcha
Also, even if you don't use it every day, practice with a high-level language that makes it easy to refactor and experiment with the design, then it will be easier to apply the same ideas to java, c++ or other bondage languages.
Marco Mariani
@Marco: Great point. I code primarily in D b/c I need an efficient, performance-oriented language, but programming in Python has taught me idioms that I go on to use in D. The idioms are less obvious in D because they require some use of metaprogramming, but they're just as useful, and once you do the metaprogramming you can dump it into some library and reuse it forever.
dsimcha
@Marco: What would be examples of high-level languages?
randomguy
For something that's also useful day-to-day, they might be python, ruby, haskell, the good parts of javascript, many more. LISP if you are brave enough. Basically, if you read http://www.info.ucl.ac.be/~pvr/VanRoyChapter.pdf you'll see that most languages only scratch the surface of the many ways to write programs.
Marco Mariani
+1  A: 

I found that once I started using Dependency Injection in earnest, my code naturally came out well-separated. The vast majority of my classes looks the same, no matter the complexity of the problems they solve. This consistency will help you develop a feel for what a class will look like even before you sit down to write it.

Another concept that helps me frame my thoughts is intent vs. mechanism. Intent is the why of a solution, mechanism is the how. You can also think of intent as what I'm doing, and mechanism is how I'm doing it. Powerful code which is a joy to read almost always has a very high intent/mechanism ratio. Concentrate on factoring implementation details away from your code's core concerns.

Finally, seek out other metaphors that help you understand ideas in new ways. A great book that contains a lot of salient suggestions is Implementation Patterns by Kent Beck.

Bryan Watts
+1  A: 

If you haven't had any formal introduction to design patterns, I'd suggest reading these two books from start to finish...

Head First design patterns

http://books.google.com/books?id=GGpXN9SMELMC&printsec=frontcover&img=1&zoom=1&l=220

Design patterns explained

alt text

no
+1  A: 

When you realize your design is causing you pain, identify where the pain is coming from, figure out an alternative solution that eliminates that particular pain, and make the time to rewrite it. Repeat until the pain goes away. That's how you improve. Unit tests help a lot with this process. Books have helpful advice. At the heart of it, though, you have to come back to things you've designed, redo them, and compare the results.

Some environments are less tolerant of this kind of thing than others. In some cases while my first version of something is getting tested I've been tearing it apart and rewriting it drastically. If you are in an environment that is totally rush-rush all the time you are going to be too harried to learn a lot.

Nathan Hughes
+1  A: 

I read/heard somewhere something like this from one of Josh Bloch's presentations: As programmers, we'r all API designers.

Now designing isn't something easy. It takes a lot of consideration and effort and re-iteration to make your design really shine. When there are time-constraints, your experience of programming and designing helps you nail down fast without going through mistakes that everyone else would do. Just begin with these two books and you'll enjoy a lot:

  1. Effective Java 2nd edition
  2. Head first Design patterns

And i'll add one more thing to it "Dependency injection is the way to go".

Happy coding.

nabeelalimemon
+2  A: 

It's something I struggle with as well, and as many people have alluded to already, I think an important skill to develop (I haven't got there yet :P) is to learn to throw away code. Be prepared to iterate and refine your designs until you're happy with them (at least, till the next feature comes along).

I know I focus far too heavily on trying to book-learn this kind of thing, but even I'm slowly coming round to the idea that reading books and even other people's code is not going to help as much as learning to iterate and letting go of bad designs. Only experience will teach you when pattern X is called for, or (even though a book might say otherwise) why Y is preferable to Z under certain conditions.

shambulator
+1  A: 

When it comes to design, there is not substitute to learning and experience.

I would recommend that you read following books, the earlier the better -

Refactoring, Improving the design of existing code - by Martin Fowler, excellent book to learn good Object Oriented coding skills. Helps immensely if you are maintaining code.

The Pragmatic Programmer- From Journeyman to Master - absolute must for beginners.

Head First Design Patterns - This book has an excellent approach to teaching design patterns. It teaches the underlying OO design principals and then teaches you the patters. Must read.

Test Driven Development - Kent Beck

Domain Driven Design - Eric Evans - a bit advanced but if you are talking about design then you should read it.

UML Distilled - Martin Fowler - UML sketching is very important during OO design discussions. This books teaches you that is the shortest possible time.

Patterns of Enterprise Application Architecture - Martin Fowler - bit more advanced.

For more - you should follow blogs of gurus like Martin Fowler.

Cheers.

Unmesh Kondolikar
+1  A: 

Another brick in the wall:

Do some Google searching for GRASP and SOLID. These are two very popular methodologies for code design that will support a loosely-coupled, highly-coherent, readable, maintainable design. In short, GRASP is a series of patterns and philosophies that seek to answer questions you generally have when designing code. SOLID attacks the problem a different way; it's a series of overarching principles that will lend themselves to good code. I think you'll find, as your projects get bigger and you get more comfortable with designing good code, that the two will lend themselves to similar designs, but GRASP is usually the easier of the two to wrap your head around at first.

KeithS
+1  A: 

I would recommend thinking of objects not as data structures or things, but rather as "people" that can send notes to each other. So a program then is more like a graph or web of these individual, autonomous people than it is a string of instructions to follow. Think of a bucket brigade dealing with a fire, for instance.

Test-driven development can certainly help, especially if you forgo "strong" mocking frameworks that let you mock concrete types or static members. Getting rid of singletons is a strong first step towards better software design.

One thing I found to be helpful was what I like to call "Ideal Environment Development" - write your classes to use the interfaces that would exist if they existed just for that class - then figure out how to implement that interface, having it talk to the interfaces it wished existed, and so on. This is usually easiest if you start with the point of user interaction.

A Brief History of Mock Objects - http://www.mockobjects.com/2009/09/brief-history-of-mock-objects.html, and Mock Roles, not Objects - http://static.mockobjects.com/files/mockrolesnotobjects.pdf are also good reads on program design overall, even though they focus on mocking and TDD.

kyoryu
+1  A: 

I'm afraid there is no substitute for a great deal of thinking and experience. This experience must include following your work from design to full implementation and maintenance.

Doing all this under the guidance of someone who is very skilled in the art of design is a big help. Sometimes they will be able to point out your mistakes in advance, and other times they will provide valuable insight during the post-mortem. If you are serious about stepping up your game, and there is nobody like this around you, you may have to change jobs.

Stretch yourself. Work on a design that seems at the edge of your ability or beyond.

Beyond that, I can't add too much to what others have posted, but there is a good trick for designing the interface to a class (or anything really): Write some code as if you are using the class. This quickly shows you which of the class's members and methods are most essential and what kind of interface will make the code that uses your class as short and clear as possible.

Using this technique prevents a common mistake, which is to expose an interface which is convenient to implement, rather than one that is well-designed and provides air-tight abstraction.

PeterAllenWebb
+1  A: 

The best thing I've done is rewrite code repeatedly to avoid all duplication and to make modifying your code easier.

As you do this you will learn things to do and things not to do. This will teach you better than reading any book or "Learning" any set of patterns/tricks to use.

At first this makes for some simple lessons--you learn to identify duplicate code and move it to subroutines/functions/methods where it can be reused. You learn to extract data from that chunk of code and pass the data in as a variable instead.

Later you may learn ways to extract parts of the code itself that varies. You learn to make more parts of your code "Look the same" so that you can compress the duplication. Your code becomes shorter, more understandable and more readable over time.

You also learn that all this extracted data (that you are passing into your subroutines) can be extracted from your code completely and almost always should be because that data eventually needs to be modified itself (it's configuration data usually).

There is a theory of "Code Katas" where you take a small bit of code and re-write it every day as an exercise. This is really good although I've always just practiced on whatever code I was working on that day instead of some pre-written "exercise".

Honestly I have never read or learned ANYTHING in 30 years that has improved my coding as much as my commitment to DRY code and desire to re-write solutions to be more usable.

Bill K
+2  A: 

On top of other answers, the "simple" path: First step: Learn! Learn from books, articles, etc. Second step: ask for review. Find someone with better experience and ask for a review of your design. Be ready to justify your decision and to accept the feedback ( even if is negative ). Last step: improve the design by refactoring the solution and go back to first step.

dragos55
+4  A: 
  1. don't overengineer it
  2. don't worry yourself into doing number 1.

Good code is stuff that's easy to maintain, understand (and works). So there's little point in trying to make everything fully OO when a procedural approach would work better, even though "OO" is supposed to be soooo vastly superior.

Stick to traditional, mature approaches - for every 'cutting edge' thought in designs and practices I can show you one that was similarly 'cutting edge' in the past and is now pretty much discredited and not used.

I suppose the best way to find good designs is to work on code. If you find you can maintain it without much grief, you have a good one. Figure out why that was so easy, and you'll learn it over time with experience. Don't expect to be taught 'good design' from a book overnight, software is too complex and too full of different approaches for that way to work.

gbjbaanb
+1 Great answer
Chris O
+1  A: 

Indeed this is a subjective question, and there is no substitute for experience.

Design patterns is a good first step. The gang of four book (Design Patterns: Elements of Reusable Object-Oriented Software) is kind of the original bible in this area but its not an easy read. So I highly suggest Head First design patterns, I have read through most of it and its excellent. Design patterns are about solving problems by dividing the problem into manageable chunks and using previously researched techniques that are known to work and are well tested. I would say they sit in the middle between your design solution and your algorithm in terms of design. There are obvious bad ways, such as the anti-patterns which are covered in the book aswell as the good design patterns.

Martin Fowlers enterprise design patterns would be the next book after this, and it is quite a leap up in experience required to understand it. I would say i'm a good programmer and scored extremely well in university but this book is not easy, but it is very useful. These design patterns are very clever, whilst the core design patterns you should consider as your bread and butter.

As a design pattern example, look up how JUnit (Unit testing) is built, this unit testing program is built using alot of patterns and as such ends up having only a few classes but is extremely flexible.

In addition to design patterns, are design concepts like GRASP. Basically these are good programming practices.

Now as to designing an entire solution, this is what you will get with experience and practice. When making solutions you will make mistakes, you will need to rewrite and refactor code but you will learn. There are many books and designing and many more following software engineering methodologies. But i would recommend you read Eric Evans, domain driven design, tackling the complexity at the hear of software. Again not an easy read but well worth it.

But the most important thing you must consider with a design is what is right for the problem. KISS - (Keep it simple stupid, i think that's right). Basically if you building a simple application in a week and are aware it will not be used in the future. Do not overdesign/over engineer the application just keep it simple. Design patterns tend to increase the number of classes in order to make code more flexible, at the cost of this can be code complexity and time to implement. So if the application is simple do not worry about writing your business logic into a GUI, but if the application is going to be built upon or reused you really need to use a GUI design pattern like MVC,MVP or MVVM in order to have a good design.

I made the mistake of over engineering my final year project. Sure it was flexible but I never had time to implement or display all the flexibility. I built everything to an interface to avoid dependency problems and it allowed me to unit test better and swap file/DB repositories on the go. Slight problem was it took 6 weeks working 12 hours a day to program 30,000 lines of code (more if you include comments/white space :) ).

It is always a balancing act. And over time you will gain both experience and knowledge, a good design will make use of these as you learn.

JonWillis
A: 

A lot of programmers I know haven't really given sufficient thought to their basic philosophy. I strive to "do what's best for my clients" without assuming I am the final arbiter of what is good or bad. I generally assume they know what's best for themselves better than I do.

For example: If a program could be made "elegant/better/cooler" by spending $2000 of the client's money with a rewrite is that better for them than just fixing a few lines of code? Unless the program is in the design phase or early in it's life then probably not. Most of the programmers I work with wouldn't ever consider it in that way. Their mantra is "Legacy code sucks. If it's not done the way I want it done then the guys that did it were stupid. I'm really good at this and I know what's better for them than they do."

I've found a lot of clients over time will recognize that I'm not just a consultant trying to extract money from them and I'm actually trying to give them advice that's in their best interests.

Jay
+1  A: 

Lots of great answers already, but one more point to add: don't design/write code that is difficult to debug. After you gain enough experience, then you'll deeply appreciate this advice. For one example, using way too many objects when a simpler design suffices. Perhaps the most interesting point of the GoF's book is their advice on being mindful of the object granularity. But that is only one example, there are many ways to write code that is difficult to debug.

Of course, while following TDD methodologies makes it less necessary to use the debugger, there will always be cases where you just have to use it.

Chris O
+3  A: 

Probably the biggest thing I struggle with is separation of concerns. I usually think in terms of snippets of functionality that do something. I struggle understanding who is responsible for what piece of functionality and how should I make that functionality "glued" together with other components in the system.

If you've read about how to do it and still struggle maybe an alternative explanation might do the trick. I sometimes find myself thinking again in terms of pixie driven development when I'm stuck. It's a fun read and may give you an aha moment:

http://lizkeogh.com/2009/07/01/pixie-driven-development/

koen
+1: I like the pixies.
Don Roby
+1  A: 

Take a look at Code Complete 2 by Steve McConnell. It serves as a very good point of reference whilst you are busy coding. I would describe my learning style as visual i.e. I like to see code examples and this book is packed with good vs. bad code examples that explain various design concepts of good software construction.

BradB