views:

3246

answers:

32

I am a junior software developer at my company. I am given a great deal of independence with supervision being limited to ensuring I am meeting deadlines and design goals. I enjoy the independence and trust I am given but I sometimes find my self overwhelmed by design decisions.

When I start a new project or given a task to maintain an existing project I often feel paralyzed by design decisions. I am always striving to make the best possible choice for the current task but I am often overwhelmed by the possibilities. I feel it is often hard to get off the ground because I am always thinking "what-if". The "what-if" can often have a dramatic impact on my current design decision or plan. This type of thinking forces me to re-think my design and I feel that I spend a great deal of time worrying about the design of my solution. I would love to be more productive but I am often overwhelmed.

How can I overcome the paralysis of design decisions? I feel a great deal of time is spent on the design when it could be spent on developing

Any advice from a senior developer would be appreciated. I would particularly like to hear about how you undertake a new project and how you decide the best design for your project.

+3  A: 

There is an appropriate amount of time to spend on design issues before you dive into coding. I usually try to stick to a given set of requirements with some obvious future proofing built-in.

Design Paralysis takes hold when you go past the obvious future proofing. You can't always plan for every use case before you start working on an application. At some point, the design is "good enough" to start coding.

Sit down, plan your software out to meet your requirements, see if there are spots to build in flexibility...then code it.

Justin Niessner
+23  A: 

Communicate. If you are facing an important decision, it is always a good idea to involve the others affected by it. Sketch up the alternatives and explain the consequences. It helps you to get a clearer idea, and makes it a common decision.

Also communicate if you are not certain that your choice is the right one. If the decision is important enough, and you cannot find an answer on your own (or with the help of stackoverflow, heh?) you can suggest to hire an experienced consultant for a short time. Alternatively, you can announce that you are going to experiment with the one design decision, and that it will take time. And that, if it does not work, you may have to start from scratch.

relet
+1 Sometimes articulating the costs and benefits of my alternatives brings things into sharper relief and makes it easier to get behind one of those options.
grossvogel
+1 For communication. Talking ideas through with someone is the best way to kick them into shape. You don't need to carry the entire weight on your shoulders alone.
Dan Diplo
Another +1 for communication. Even if you are communicating with someone less experienced, ideas might spring up when you talk it out.
Hippo
+8  A: 

I feel a great deal of time is spent on the design when it could be spent on developing.

I would argue that this is just what you want to do. If you are doing a more agile type of development, the design will be more iterative, and the amount of time required for design will vary depending on the project, but you still need to do design before you start writing code. If you don't know what problem you're solving, how are you going to solve it?

With that in mind, I'd recommend some reading over at http://www.agile-process.org/. I think you'll find some ideas there. One of the concepts I really like that's discussed there is that you plan and make decisions based on what you know at the time. When you have more information, you make a new plan. Thus, design is not a phase that you go through and leave, but something you continue doing throughout the development process. (I'm a bit biased toward using at least a bit of Agile in my work, and I'm at a shop now that uses a version of it.)

Feanor
@Shiftbit: 1. As far as that list goes, I can recommend Alistair Cockburn's book "Agile Software Development" as well as Martin Fowler's "Refactoring." A classic book on object-oriented design, with a lot of information that can be applied to software design in general, is Rebecca Wirfs-Brock's book "Designing Object-Oriented Software".2. Definitely; when I haven't understood the problem sufficiently to formulate an reasonable solution, and then found it was too late to change the plan because I didn't review to make sure the plan was right, things have turned out badly.
Feanor
I can't think of any programmers that doesn't continuously doubt about the design decisions while writing the program; though depending on the flexibility of the development process, he may or may not be able to dump the code he had written when a more mature design crossed his mind.
Lie Ryan
+58  A: 

Awesome question.

I believe the key is organization and time management. Keep in mind that it is not always necessary to devise the absolute best solution for each component. You can spend lots of time on study and research, but when it comes time to actually Get Stuff Done, you can only execute based on your current skillset level.

Some ideas:

  • I LOVE Kanban. If you haven't heard of it, look into it. AgileZen is a great free option. This will help you get your tasks organized.

  • Separate your knowns and unknowns. This will allow you to focus on areas of lower confidence in your solution and get them out of the way first.

  • Break up and isolate your solution components so you can write little test prototypes quickly to prove out ideas.

  • NEVER fall in love with an overall solution, or any component of one. If something isn't working, be ready to throw it out and try something else (but don't give up too quickly.)

  • Test Driven Development!! Writing tests as you go keeps things nicely compartmentalized and should increase your confidence that your code will work, even if the design isn't the absolute picture of perfection.

  • Design for the long term, to the best of your ability. Software is art, and as Da Vinci once said: "Art is never completed, only abandoned." Version 2 will be better than version 1, and so forth.

Dave Swersky
@Dave Swersky: I really like your suggestion about Test Driven Development. If you can encapsulate as much as possible and demonstrate that it works, then you can focus on integrating it. Do you have any books that you would recommend?
Shiftbit
The Art of Unit Testing by Roy Osherove: http://www.manning.com/osherove/
Dave Swersky
Love the parallel between art and programming. Sometimes when I'm hashing out a solution, it feels a lot like when I'm sketching in my art pad. I have a rough idea of what I want, but how to get there. +1 and great answer. :D
DarkBobG
+1 for the Da Vinci quote. Very accurate!
chiccodoro
@Shiftbit Kent Beck's Test-Driven Development by Example really addresses your concerns head-on. It's all about small victories and iterative re-factoring, enabling your design to grow organically instead of requiring that it spring from your head fully formed.
bshields
+4  A: 

Making the right design decisions depends a lot on your personal experience. The longer you will work in the business, the more experience you will get and the easier it gets to make design decisions. I do not think there's a real shortcut to this. The best advice I can give you: if you are really stuck with a decision, ask others (for example, here on SO). That won't take the responsibility from you, but you get more pros and cons and perhaps a different perspective.

And don't forget: there is often more than one correct way to solve a problem, and sometimes any decision is better than none.

Doc Brown
+5  A: 

Agreed. its a great question.

Great questions don't have definitive answers. I certainly don't have one to this question; and in fact even after well over a decade of working in soft eng its one I ask myself and others and struggle with on a weekly basis :-(

I work with C#, Java, SQL and web kit (XHTML,JavaScript, CSS). Some personal thoughts follow. Apologies if they appear crass or patronising. I make no assumptions about your age or experience and they're things I tell myself everytime I start a new job/project. In no particular order then:

  • Try and keep a design as simple as it can be while still being able to do what it needs to do.
  • Get to know your source control system. Never throw anything away! You can get a system running in hours by tweaking a branch of an existing project.
  • If you don't have one already, set one up! ;-)
  • Free up time to ensure you give your projects an adequate design phase. Automate the actual code generation as far as is feasible. If you don't already, take a look at tools (or write one yourself) to do things like generate classes from databases.
  • Try to build up a library of common/utility code as you write stuff.

    • Reuse. Don't reinvent wheels. Jobs like file I/O, database access, emailing etc. can be done through your own custom objects or common routines you drag & drop from your own library. Can your IDE(s) handle code snippets?
  • Have a look into TDD - if you're lucky enough to get time to do R&D -type work in your day job you should make the most of it. Working in TDD gives you discipline, goals and targets and consequently efficiency and speed. Working in a TDD way can release a lot of time that would/should be spent going round in circles trying to produce a definitive design document before any actual works gets done... And of course frees up time that would be spent designing and running tests in more ad hoc development methodologies.

  • Get your code working right first. Restructuring code into a different configuration e.g. adding extra tiers, rejigging class libraries and so on isn't as difficult or tiresome as requirements gathering, algorithm design and implemention and testing. Focus on meeting the immediate requirements of the system and try not to worry about how beautiful your code looks or how fast it runs. Development time is not tweaking time. And you're a developer, you'll find time to go back and fiddle with it :-)

  • Try not to get seduced into wasting time learning how to use fancy new technologies if those you know and trust will do the job in hand. Porting applications from one language to another isn't so bad so long as the original is finished.

  • Software development is a kind of engineering, there is a lot we can learn from more established engineering disciplines e.g. http://www.raeng.org.uk/education/vps/pdf/design_principles.pdf. Read around the subject!

hth. i'll be keeping an eye on this thread for tips myself ...

5arx
@5arx: Great answer and very thorough advice.
Shiftbit
+44  A: 
GrandmasterB
... and good is the enemy of great. :-)
pc1oad1etter
+4  A: 

As a fellow junior developer, I've survived on two mantras:

1) Get the job done (to keep my job)
2) Get the job done, better (make it maintainable and reusable)

The second item is exactly the same struggle you are facing and I can relate. And from what I've seen in my brief tenure is that as long as we care about design, we are light years ahead of some 'senior' developers.

Regardless of your initial design decisions, you will always be able to look at it with fresh eyes down the road and find something that could have been done a little better. In the end, the functionality stays the same.

softpro_sea
+13  A: 

What ever you do will be wrong in some way

Not as a classical quote as GrandMasterB's Voltaire quote, but it is a bit more pragmatic. when you realise that there is no perfect solution and you will screw up what ever you do it frees you up to chose any solution.

However don't take this as license to write any old shit. Only employ it when you are comparing well thought out solutions that pull you in different directions.

Of course the more experience you have then the "less wrong" your solutions will be, and one way to guarantee gaining experience is to write wrong solutions and understand where you went wrong.

Peter M
+1: And another quote: "Good judgement comes from experience. Experience comes from bad judgement."
Don Roby
+3  A: 

When I was a junior developer, I thought that if I wasn't writing code, I wasn't working. There's always the pressure to just start coding, but time and time again, I find that when I took a notebook and some paper and scribbled out some use cases, I found out things with my initial design that didn't account for something.

Now I spend a lot more time sitting down first, trying to break up the design into independent components, things that I can refactor separately if I need to.

So, the advice I would give is this:

  1. Spend more time designing than you feel you should; as long as you're not developing frameworks for your frameworks, you're probably not overdesigning.
  2. Modularize as much as you can, so that if you realize your design is insufficient or inelegant in one section, you can go back and refactor the individual component.
  3. Try to minimize the number of times you say "I'm just going to hack this for now and I'll fix it later..." because, in my experience, that's hard to do. Fixing design mistakes you made previously doesn't earn any revenue, and it doesn't bring in any new customers, so it always gets prioritized lower than new features.
Shawn D.
+5  A: 

As a fan of Agile develop methods, I'd suggerst the idea of getting a prototype up and working and then refactor, refactor, refactor. Recognize that whatever you make may be good but not awesome in that first try as generally most requirements will evolve over time and this can be a hassle but that is what managers and directors handle at times, is someone wanting something customized when there is little value in the change at that point,e.g. think of cosmetic changes rather than functionality changes like the developer is hanging up a picture on the wall on someone goes, "A little to the left," or "A little to the right," or alternates in some random order between them over and over again like that Energizer bunny.

I've been building applications for 12 years and I still think getting something up and functional is a great starting point. Then comes the refining the heck out of it where you may end up gutting 99.99999% of it as you go through changing requirements. Is it so bad if some early version doesn't work great? I know some things will work well and some won't, and part of my job is to try this and that and see what happens.

Have a little faith, take some chances and build your intuition.

JB King
+8  A: 

I don't know that I can give you any great over-arching answers, but here are a couple of tidbits that come to mind:

One: Concentrate on the data store. Whether the application uses databases or flat files or whatever, concentrate on getting this part done right. Typically you have many programs or modules that access one database. If you have a good database design, 5 good programs, and 5 bad ones, you can fix the bad programs one at a time while everything else continues to work. But if you have a bad database design, it's very hard to fix because you can't change it without having to rework all the programs that use it. (Maybe this is just a bias on my part as the author of a database design book, but I think it's a valid approach.)

Two: Cultivate laziness. Not sitting around playing video games, but the simplest solution to any given problem. Way too often I've seen a programmer presented with a problem, come up with a very difficult and complex solution, and dig in to implement it. If your solution seems very complex and difficult, step back and see if you can't think of a simpler solution.

Example that comes to mind: I once worked on a system for doctors offices that included appointment scheduling. The office would say how many appointment slots they wanted per day and the system would then make that many time slots equally spaced. Our clients complained that it was coming up with appointment times of "10:07" and that sort of thing. They requested that when we printed out the appointment slips that they gave to the patients, that we round all appointment times down to the next lower ten minutes, i.e. 10:07 should print as simply 10:00, 1:23 should be 1:20, etc. A programmer was assigned to make this change. Several days later she came to me asking for help: The program had become so big and complicated that it wouldn't run on her computer any more. When I investigated, it turned out that she had basically copied all the code to set up the appointment schedule into the printing program, so now it was recalculating the entire schedule every time she printed out one appointment time. She explained that she had done this so that when she did the time calculation, she could add the function to do the rounding on the time. I asked, "When you print the slip, you get the appointment time as a character string, right?" She replied that she did. "And you just need to round it down to the next lower ten minutes, right?" Again, yes. "So," I suggested, "Why don't you just change the last digit of the string to a zero." "Oh," she replied, "I didn't think of that." It took one line of code.

Jay
@Jay: Great example and good advice.
Shiftbit
+1 for focusing on the data model/store, and +1 for avoiding unnecessary complexity
ObiWanKenobi
+3  A: 

It's all about experience, and you get experience by writing code, being humble, learning from your mistakes and learning from others. Meet the requirements of your job and write lots of code. You'll get there :)

MalcomTucker
+2  A: 

I manage two junior developers who at times suffer from over-designing new software. The fact that you've realised there's a problem means your half way to being a better programmer.
I can only offer two bits of touchy feely advice:

  • Get to know yourself better. If you're the sort of person that is always asking 'what if?', then use that to your advantage. When trying to design software, write down all your 'what ifs' and keep going until you can't think of any more. Then number your 'what ifs' in order of importance. Cross off half of your least important 'what ifs', base you design on the remaining ones.
    Really, its about taking a structured and methodical approach to all aspects of programming, and cultivating enough good judgement, to keep the good ideas and throw out the crazy ones.
  • Talk directly to your manager about what you're experiencing. If s/he's worth their salt, they'll try their best to make you happier and more productive, after all it's also in their interest too.
Jonathan Swift
+4  A: 

I'm reminded of two complimentary ideas. The gist is think a bit but don't let it stop you from getting started. Just trying something out will often bring up an issue that thinking wouldn't. In that vein here are two things to think about as you plan:

  1. YAGNI, or You Ain't Gonna Need It. What this is basically saying is do the simplest thing that could possibly work. When you find that it's not quite right go fix the case that's broken. This fits in with Test Drive Design as noted by @5arx.

  2. The second in "Red, Green, Refactor". This says figure out what's wrong and fix it. Once you have something that works the next step is to make it look nice.

If your code is beautiful but doesn't work no one will want it. If it works you can start to get feedback and use that to fix things up. You'll also start to see what works and that will color your future decisions. You'll find yourself making better decisions the first time more often.

Like the old joke, "Taxi driver, how do I get to Carnegie Hall?"

"Practice, practice, practice"

Paul Rubel
+2  A: 

The simple rule is:

If you don't know whether one design is better than another, that is the same as there being no difference between them for your purposes. You may look back on a choice with regret, but only if you make that choice to begin with. It is literally better to choose the wrong approach than to worry overlong about the right approach. You can fix bad, but there's nothing you can do for not yet implemented.

Mike Burton
+3  A: 

I'd say communication is the key. I've learnt a great deal just by seeing how other people would approach a problem.

If that's not always possible, someone once told me about a method where you just explain the design decision you face, the possible solutions and their pros and cons, to an inanimate object. Just by articulating your thoughts in a different way, it sort of makes you see things in a different light. And make you look like a little bit of a crazy person to boot :)

Apart from that, prototyping is brilliant once you've got an idea you think will work, just try and build something with that principle and you should spot any obvious flaws.

Fiona Holder
+3  A: 

A design is the structure of a program. That program exists to satisfy a certain need. So first, you will want to understand the requirements. Typically, you will have different kinds of users doing even more different things with software.

From that, requirements can be prioritized. It doesn't matter much if there is a bug (with simple work-around) in a feature that is used by one person once a year, but a similar bug in a feature that is used every two minutes by 10'000 users is really annoying.

Also, you will have some ideas on how requirements might evolve. However, keep in mind that your understanding of the stakeholders is incomplete, and that they might change their goals as a result of future events you could not have predicted. Chances are, therefore, that the new requirements will actually be quite different from what you expected. Designing for anticipated requirements therefore is a two-edged sword: If the anticipated requirements become actual ones, you have saved yourself work - but if they don't, you have expended additional effort for zero benefit. In fact, not just you, but also those that inherit your code that got needlessly complicated because it was designed to do more than it had to.

Next, I briefly think about the feasibility of the requirements, the rationale being that the earlier I communicate that something is not possible, the more time I give decision makers to come up with alternatives. If something is obviously impossible I give feedback right away, if it might be impossible I try (briefly) to determine whether it actually is. If that remains inconclusive, informing the decision maker about that is prudent. You might then implement that functionality first, so that if the project must fail, it at least fails early.

Then, I try to identify similarities among requirements. These often result in similarities in the code, and are often an opportunity for code reuse. I then group requirements according to similarity into units. Then I identify dependencies among units, and finally implement unit by unit in dependency order.

To implement a unit, I first design the external interface. To get a good interface, it must conform to the expectations of the caller. So I put myself in the mindset of the caller and write the interface signature, before my mind becomes "tainted" with the implementation details the interface is supposed to encapsulate. Some people recommend to actually code against this interface at this time by writing a unit test. (I rarely do that, because I my domain units often require a lot of mocking to test, and when the testing code gets much longer and more complicated than the code under test, and most bugs are integration bugs anyway, integration tests are more efficient.)

Then, I implement it.

Then, I test it, and fix bugs (Because I construct the units in dependency order, I always have a runnable program I can test against)

And then, on to the next feature (or the week end, as the case may be :-)

meriton
+2  A: 

I like what others have written but some of it may be difficult for you to implement. For example it may be difficult for you to become proficient in Agile processes or PM or Test Driven Development tools while working on your project.

So if you're really struggling you may want to simply give yourself dates and stick to them. e.g. if you've got three months to finish your project. Give yourself 1 month each for analysis / development / stabilization. Its far from perfect but its an easy way to get out of analysis paralysis as it has no dependencies.

Conrad Frix
+6  A: 

I think the key to finding the most suitable design for a project is to be always prepared and willing to redesign some parts of your code whenever you see the need for it or a better design becomes apparent from the code you already have and the requirements you want to fulfill next.

I noticed that most ambitious junior and also many senior developers tend to invest too much into design decisions in a too early stage of a project. This is often accompanied with a rather strong believe in the superiority of the arbitrary chosen design even when it later turns out to be less optimal than originally thought.

In my experience it is simply not possible to make the perfect design decisions at the very beginning of a project. There is still too much uncertainty of what will actually work out well. This is why I try to avoid to commit to a certain design as long as possible by choosing the most simple approach that allows me to implement the aspect I want to complete next and also by not investing too much time into a complete suite of unit tests that rely on a certain still imaginary API.

When I start a new project I like to begin with the implementation of some of the most algorithmically challenging central parts in simple demo programs driven by a main method with some simple testing functions that are rather meant to help me debug and profile the code than to verify the correctness of the implemented functionality in the future. I deliberately avoid writing unit test for this code at this stage as unit tests tend to cement the currently existing API. I rather try out several alternative ways to implement the same functionality that might depend on differing data structures to get a feel for what might work best. These simple test programs usually already suggest some design to wrap the implemented functionality and also offer some means to evaluate the feasibility and capability of the project at a early stage.

Based on this I try to keep the next steps simple and straight forward and to get as much new code working that actually contributes to the tasks of the project rather than to infrastructure that I might not need at all. Only if I have a working but still quite hackish prototype of some larger portion of the project I begin to really think about the design that would fit better for what I already have and than I redesign and rewrite the parts that can be improved. At this stage the developers involved have at least gained some credible understanding of the required data flows and interfaces that are needed to drive the implemented functionality. Here it is still possible to move to a different framework or to do similarly drastic changes. After this it is time to begin to write some basic unit tests for this part of the project.

The more the code gets refined and tested and the more the different parts grow together the more tangible design decisions must be made and this is where I spend most of the design time to redesign and even partially rewrite large parts of already running code to make it fit better or just more elegant. This might sound like wasted time if I had chosen the perfect design right from the beginning but with 20 years experience in designing and implementing rather complex projects and frameworks I gave up that dream and adopted myself to the less perfect reality.

This approach might sound as if it only works for very small projects but it actually works for larger projects too. Even large projects should always start with few core developers that build the foundation of the code and further developers should only be added when clear distinct tasks can be defined.

x4u
+3  A: 

It's important to know what your requirements are. That will help to avoid what-if paralysis. If you find yourself with intractable what-ifs, ask someone appropriate (manager, team-lead, etc) to help you make the call.

Once you understand your requirements, build the simplest thing that will work first. Something important to keep in mind is that your code is likely to change. You don't try to figure out what the perfect way to express something is, because odds are that you'll need to modify it at some point.

Paul Morie
+11  A: 

One advice:

Write a prototype with the intent to throw it away.

Non-trivial programs cannot be designed up-front, there are aspects that you can only know once you've actually written it. Of course, an up-front design is still necessary, but don't get stumped on that, since it is impossible to think of all the "what if" scenarios.

When you feel you're getting stuck in design paralysis, then stop designing and start writing the prototype. Over the course of writing the prototype, you will learn the many flaws in your original designs, and use this newly acquired knowledge to design the next prototype. Repeat doing this design, prototype, design, prototype, ... until you feel confident about your design.

The most important point about writing a code to throw away is: don't feel too guilty on writing quick and dirty codes; these prototypes would not end up in the end product anyway, so some quick and dirty codes is fine. In fact, if you write first-class clean code for your prototype, you (or the upper management) may end up thinking "the prototype is good enough" and the prototype get turned into production code; avoid that situation by writing q'n'd code.

Lie Ryan
I like this; You learn an awful lot by writing mock apps to test a new idea.
Carlos
In the scoping/design phase prototypes are sometimes essential. It's really the only way to commit to something that you have no idea will work otherwise (especially if the task is something new and undiscovered).
Dimitris
This is great advice. I started applying this concept yesterday and I found it really liberating. This removes the pressure of planning the perfect design. I can develop a small prototype and test it against real data. This allows me to see what worked about the design and what needs to be improved. By expecting to throw away my prototype I can develop a quick solution that demonstrates the overall process without having to worry about patterns and design principles. The detailed plan can be prepared to handle the intricacies after I've seen a basic implementation of one possible solution.
Shiftbit
+3  A: 

Personally I spend some limited time to think the idea through and than just implement it. I try to make code as easy to change as possible because I will be changing it, a lot, in the future.

My idea is that you can't think of everything unless you actually use the thing you're programming. You can't think of every use case, every bit of functionality, every bit of buggy system API you will be relying onto unless you implement the idea you have and let it run for a bit.

It does not follow somewhat popular "design everything before you implement something" pattern simply because in my experience you can't do it no matter how good you are. You may think that it takes more time to write good code using my method however, again, in my experience, it doesn't. The time you would spend thinking and thinking and thinking would be spent on coding. And it will allow you to understand your design problems better IMO.

Sorry for somewhat incoherent post :)

Steam Trout
+2  A: 

I'd agree with a lot of the stuff written here. I think the important thing is to make the decision - think about it, consult people, whatever, but make the decision. You can never make the right decision because there's always pros and cons, but that means you can never make a conpletely wrong decision either.

My only addition would be that you should consider this with one eye on the purpose of the program and who will use it. If you're writing a command line utility that only you will ever use, and for testing purposes, then I wouldn't worry too much about the design.

If, on the other hand, you're embarking on a system used by hundreds of people, or a system that will be used to navigate a fighter jet or something, then you should probably spend (a lot) more time on design that development.

Real life is usually in the middle.

pm_2
+3  A: 

It's unlikely I can really make a dent on the body of knowledge in this thread, what with so many great answers here already, but here is a slightly different perspective.

I've found in the past that whenever I'm overwhelmed by the what-ifs of making one design decision over another, the most useful piece of information I can find to help me come to terms with my choices is a better understanding of the business problems my code is intended to solve.

I start looking up towards the product vision, not down towards the technical requirements. I ask myself "how is this thing going to be used? What does the everyday user do, how does he interface with the software, does he really care about whether I do x or y?". This is important because, if you can't make a logical decision from a technical perspective (because you don't have the knowledge or experience), then at least you can try to make a decision that aligns best with the users -- not that the two should ever be in conflict anyway.

So at the end of the day you'll probably have to make a decision to do something that is not necessarily any better than the other choice, and doesn't answer all the what-ifs, but at least you know you have a valid reason for making it. Once you learn to make choices in these types of situations, you'll be able to make them faster, and get used to resolving your inner conflicts more efficiently once you realize your mind is going in circles.

With time, as you wrestle with these types of decisions, you'll accumulate more knowledge and experience about the technical aspects and implications of your design choices, eventually getting to a point where you can more intuitively and logically marry the technical design choices with what's best for your users...

Gus Melo
+2  A: 

See if you can setup some pair programming in the early stages of your development with a more experienced member of the team.

Daz Lewis
+3  A: 

Shiftbit,

I'm probably facing the exact same problem right now. I'm no longer considered junior at my company (been here for 4+ years), but of those 4 years, 2 where spent re-platforming a legacy system, which means that I'm unexperienced in designing applications.

Right now I'm a month (at least) behind schedule and have let two guy's of my team idle during the last week because I couldn't decide on the project's architecture. As some pointed out, the great problem is that there is no right choice when talking about software; There are a large number of ways of adding 2 and 2, and that isn't always 4.

Some stuff I'm learning:

  1. Save a determined amount of time to making designing decisions up front, like 10% or 15% of your whole schedule.
    • During this time, write up possible solutions, and stress trade-offs. Check Eric Lippert's recent article on Graph Coloring to see how to go about this. He makes very objective trade-off analysis when building the applications.
    • Like Einstein once said, "Any man who reads too much and uses his own brain too little falls into lazy habits of thinking", so, at a certain point it's time to drop the blogs, the books and the teachers and get your hands dirty.
  2. Document your choices and rationales. You might question yourself why you choose a certain way
  3. Familiar is better. If you don't know which way to go, choose the one you are more comfortable with. It might not be the best way to go, but you will waste less time adapting.
  4. Ask for senior advice. Maybe that isn't possible, because there are no seniors at you company. If this is the case, try to call up friends or colleages or event former teachers. This can be done in the first or second week of the project and won't take up much time.
  5. Plan (and code) for change. Many projects alter their goals mid-schedule. This isn't bad, it's a characteristic of software development. Be ready to change your mind and your products at least once during development, although, of course, this should be done with care.

Once your time is up, check your notes, make the decisions, set your view and start coding. Try to preserve this view as long as possible.

Bruno Brant
+2  A: 

One of the biggest things to get over when you lack experience, is staying focused on the big picture. Too many times our vast amount of knowledge and information known to use or available to us interfere with our ability to use wisdom within the design phase. If you're asking questions this deep, you already have the wisdom required to answer the basic questions and get the job done well.

Part of seeing the big picture and keeping it in focus is getting core priorities set for the project. The basic ones should be provided to you, but sometimes those in a position to make the decisions don't know what they want. So, whether you have to answer them yourself, or someone else is providing them, a list of priorities should be made. For example, if the database I/O performance is more important than RAM and CPU utilization on the host system, then focus more on the data layer than the design time on the processing side. Or, if it's more important that the project be completed soon and fill a gap than it be the performant, then focus on the simplest solution to the problem.

Focus on your knowns, not the unknowns. Many vague project requirements create too many unknowns, which create too many mental traps. Remember to focus on the big picture, so you can roll right over the mental dips. The priorities should be the directional signs to finding the areas that you focus on the details. Those details will be easier to iron out, prototype and deliver, when you aren't getting side tracked by the idealistic solutions.

Remember, development is never idealistic. Even the knowns become unknown in a live environment. So, even the most experienced developer will have slip-ups and problems with their master architecture. Since you're not in control of every variable within an environment, you can't plan for every variable.

Also, keep asking questions. Find a mentor. And remember that the best and cheapest kind of learning is learning through other people's experiences.

Benjamin Anderson
+3  A: 

From my experience, the best way to deal with this is to break down the problem into smaller pieces and for each sub-problem, just pick a solution that works. It doesn't need to be the best/optimal solution. That way, you keep making progress overall and also don't get stuck trying to create the "best possible" solution.

Over time, once you get into the habit of getting things done, you will also start to appreciate good design and it will become easier to discriminate between approaches. Learning is all about experiences, both good and bad. If you make mistakes, analyze them and don't repeat them next time.

With enough experience, these design decisions will become easier and second nature. Sorry. no shortcuts, but I'm sure you weren't expecting any.

Ashwin Phatak
+1  A: 

As Daz Lewis said, Pair programming will be very helpful. It helped a lot in my early stages. Also, try to manage time by splitting the tasks into smaller once, spend time accordingly. Always, have Plan-B.

Avatar
+2  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 -

  1. 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.

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

  3. 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.

  4. Test Driven Development - Kent Beck

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

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

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

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

Cheers.

Unmesh Kondolikar
I own Head First Design Patterns. I will try to read most of the books you have suggested. Thank you.
Shiftbit
+1  A: 

Generally I find that indecision comes from not having enough information to make a correct call.

Designing something relating to something you are very familiar with is much easier than for something unfamiliar.

There are a couple of things you can do - apologies if this repeats a lot of what's above.

  1. how good does the first version need to be? Can you afford the first prototype to be a learning experience e.g. of the user interface and business needs
  2. can you separate out/encapsulate parts where you need more information? You can re-implement them when you know more
  3. talk to people (including people with lots of experience & users if possible) to get more information, have an understanding of what missing information is creating 'ice to crack through' and capture it somehow.

If you can design your system so that you can pospone decisions or change them easily should make it easier to maintain and understand as well. If you have a complex design that locks tightly to your initial mental model of the problem, it's likely that it'll be hard to change as new information comes available.

Greg
I remember when I first started programming with QBasic. I never really had a plan other than my end state and everything seem to come together. I suppose that is similar to a prototype. Now that I've finish college it seems daunting to start a new task. Mind you, when I started I had no concept of patterns, nor care for code maintainability. I agree with what you have said but I find it ironic that the more I learn about programming the harder it is to program.
Shiftbit