Related
I'm trying to build a very light re-usable framework for my games, rather than starting from scratch each time I start a game. I have a component driven architecture - e.g. Entity composes a Position component and a Health component and Ai component etc.
My big question is whether my model composes view components to allow for more than one view of the model, or whether to use a truer MVC where the model does not know about its views, and they are managed externally.
I have tried both methods but if anyone knows the pros and cons of each approach and which is the industry standard, it would be great to know.
depends on your audience, game devs, myself included aren't very used to the MVC model, although most know it, it's not as easy to keep it clean cut, because of development casualties (not any serious technical reasons). So from experience, I've seen dozens of game frameworks start as MVC, but only a pair were able to maintain it until the end. My theory is MVC adds too much complexity and little benefits for small throwaway games (with normally a few devs), and it's to hard to keep really cleanly separate most game objects into these layers for large/complex games. And since games have a release date, they many times sacrifice code clarity and reusability for performance and quick adhoc solutions (that will get rewritten if necessarry in the sequel (if there is one)).
However, with the caveat above, it's better to aim high, because if you succeed it's better :) and if you fail, well to bad. So you should probably try the MVC, but don't worry if it fails, profesional game devs have all failed at the task many times :)
I’d certainly vote for the model to know nothing about its views. Loose coupling is good: Simpler model code, easier testing, more choices.
I know this question might be outdated, but I need to reply on it.
Actually, I started programming a game in Lua (with LÖVE) and I started programming a MVC - Framework for it.
At first, to use MVC really depends on what you want.
I know my problems with game programming, when the program becomes bigger, and mostly the structure becoms too complex to maintain.
Next thing is, I know that I will change all the graphics when I find an artist who is willing to work for it. But until then, I'm gonna use my own dummy graphics.
I want the artist to feel free to do what ever he wants, without beeing dependend on any resolution or color restriction.
That means, I might have to change the whole (!) presentation code. Maybe even the way objects interact (collision detection, f.e.).
The game logic is captured in the models, so I can concentrate on that. And I think game logic is the most important part of making a game. Isn't it?
Hope you see my point.
But, if you have everything together: all the graphics, sounds, the whole thing; then you can code straight forward.
My MVC is a configuration-over-convention-ass, that slows down prototyping a bit.
BUT(!) iterations of development can be made much more easily. Testing, especially Unit-Tests are done much more faster.
I would say MVC turns you development-speed-curve (which is normally an anti-exponential curve) into an exponential curve. Slow at the beginning, but more and more fast at the end.
MVC works really well for games, at least for my games which are designed for cross-platform.
It really depends on how you implement it in order to get the benefit.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 3 years ago.
Improve this question
The question might seem trivial, but it's an actual problem: when you're working on a project, do you do any kind of architecture design before actually starting coding? Do you spend much time working together with a customer to get a detailed specs/usecases/mockups?
During coding, do you alter those architectural decisions made before? Do you go back to the customer with new set of specs/usecases/mockups?
I'm wondering, what's a good balance between all those non-coding actions and coding itself, from your experience?
Update:
Ok, so from the anwsers so far it seems like there are 2 approaches:
design early, then sit and code to avoid late fixes
minimize the design alone part, instead do iterative development (agile methodologies seem to prefer it that way).
I guess which way to go depends on the project, team and customer... am I right?
That which minimises the total time spent ;-)
It heavily depends on the kind of project, but generally speaking it's better to "waste" time over-designing and specifying requisites than finding out later that something was wrong and come the whole way back to fix it.
I read something about quantitative measurements of the impact of poor design decisions in "The Mythical Man-Month" or maybe in a book called something like "Software Requirements Pro Practices" from Microsoft Press, I think the time wasted in a late fix (near product delivery) was about 10x than in early stages.
If you do agile, design and coding are the same thing. In my experience it is good to pair program during the very first stage of the project...
Have a look at scrum, agile and waterfall. This is related to project management not programming per se.
Architecture also becomes easier once you have built enough applications within a domain or a platform. In PHP, if you use Joomla, Symfony or codeigniter then your scaffolding and architecture is already in place. Same for ASP.NET MVC.
My personal experience tells me that you should consider different factors. There's no silver bullet. My personal list follows, grown mostly by experience.
If you are developing something that is well known in details, the development team is sparse and with difficulty to communicate efficiently all together, the team has strong or huge dependencies towards the work of other teams, and what you are developing has a fundamental long term importance that will be difficult to change in the future (eg. file formats), go for a very long design phase, akin to a waterfall model. Also, you should spend a lot of design if you plan to develop a rather complex application, and you have to deeply consider all the possible interactions between features before coding. Coding takes very little time compared to design. Also, you should consider this if it somehow important to keep efficient record of how the application behaves from a very high level point of view, and if your team tends to be highly unstable, so that your knowledge stays on paper, rather than in people's brain.
if you have to implement something brand new and to do research on, you want feature as soon as possible, growing the application from fast feedback, you have a pool of geeks that work in the same room, are very committed to your cause, love programming and they are passionate to share and build together, go for agile methods.
if you are in between to the previous two cases, go for an iterative approach. I normally choose a 3 months schedule. When I code alone, I work agile-like, mostly because I have to cope with frequent disruption, so I add feature by feature. However, I release iterative, namely I don't plan to do an official, stable release before the third iteration. I want space to learn the field, do mistakes, and correct them before committing to maintain some stupid choice.
if you code in academia, you are screwed, because you have some of the issues in 1 without the manpower to accommodate them, and some of the issues in 2 without the easy communication required by agile methods.
roughly 50/50. whenever ive analysed my project schedules, it turns out about 50% of the time goes into design, project management, quality control, and auxiliry tasks. the remaining 50% is coding. if i dont see that 50/50 ratio, i worry.
mind you, this is using traditional waterfall model (which is more suited to custom-app development). agile methods are better for shrink-wrapped software in my opinion.
I would say it's roughly 50/50, no matter the "methodology" or project type. It only varies in how those 50% design are distributed. And that may depend on the project, but most of all it depends on the people who do the work, and how they are "wired". It's more a matter of psychology than methodology.
Some people (I'd say the more cautious characters) need a more detailed mental map before they start coding. If they don't have that map out of prior experience, they will need more "investigation" time up front.
Others yet like to just "jump in" into coding with only a rough mental map, and work out the details as they go.
Somewhere in between is to do the elaboration via spikes and prototypes, and develop the "big picture" on top of that running code. For me personally this tends to yield the best results, and the least waste. (After all, prototyping is, in a way, a test-first approach applied on solution ideas. You get an idea, test it out in a spike or prototype, then implement/integrate it with the main code base.)
My advice is: Find out the style that feels best to you personally, and stick to it. That's going to be pretty sure the style you are going to be most effective with.
Those two things are tightly coupled. Well at first stage, you are definitely will spend some time to make design decision. Then you will have to start coding and almost in all cases you will came up with some improvement decision for your previous design.
After all it will depend on delivery date and how much time you have at all and then to decide accordingly how you going to balance it. In general you make a startup design and then during coding you will update and change it. Also is a good practice to deeply involve your customer in design decision during development stage to force him be aware of it and how much of your time you will spend on each change.
The longer the period between when you write your specification and the time you start coding will increase the chance that requirements will change. So, to answer your question, as soon as possible....
If your suffering from too much requirement creep then I would suggest implementing smaller iterations of releases (if possible) and then creating new requirements/specifcation documents for each of these samller phases.
If you can't do this.... make sure you have a good change management process sin place.
My google-fu is failing drastically, but I recently read something to the effect of:
"Spend 6 months coding, 6 months designing and 6 months testing. The good news is, they're all the same 6 months."
It's important to design enough to have a map of what you are trying to code, and how it relates to the rest of the system. You can't just code most large projects - they're too big, and usually involve multiple components. I've done that when I was young, and you end up with a big ball of mud, or stay up all night for a week refactoring it.
What I tend to do now is design down to the package level, and assign roles to components. On large systems getting to the component selection stage can take several months, and involve some trail and prototyping coding.
Then the APIs and implementations of each package are evolved, based on what messages the functionality require, and how the clients of the packages evolve to cause the emergence of further requirements or constraints. I usually evolve an API by designing a pure interface (by writing the code for it) with unit tests for each known use case, then implement it. So there is some writing of code involved in designing - the best representation of the API is usually the code and inline documentation, and it's easiest to confirm that the client can perform the actions required to satisfy a use case ( and the code to do so is not excessively complex ) by writing code which exercises the API in that way, and that code trivially becomes a unit test for the implementation of the API when it arrives. But the code written during 'designing' isn't the code which supplies the implementation of the API. For APIs with low coupling ( so can be changed without breaking too many clients ), I'll switch between designing and implementing modes rapidly; for ones with higher coupling, I'll typically publish the API and use-case examples for peer review before committing too implementing them.
As aleemb said, this really is a project management question. I suggest you read up on several methodologies, find the useful and not-so-useful parts of each, and evaluate your own circumstances (team size/experience, customer engagement and commitment levels, what's done in your organization, schedule/budget, etc.) and come up with the best schedule you can. It really all just depends on your specific circumstances.
Think about how many people are going to be involved in writing the software.
If it's just a one-developer job, maybe take a smaller percentage for design. If you're going to have 30 people working on it, you probably want a lot bigger fraction for the design.
Getting teams of developers to write software is much like partitioning software up across multiple CPU's - you are going to get the best return for added CPU (read 'developer') when you can minimize the necessary communication between them. You sure don't want to get 10's of k-loc into your project before the developers start discussing architectural issues.
Now you could probably also make the case that, when you do a better job with the design phase, the coding will actually take less time and be less painful. Measure twice and cut once, and all that.
Also, you probably should think about the likelihood of the project being 'put on hold'; design artifacts have much better shelf life than immature code.
Depends on your chosen methodology.
Traditionally with Big Design Up Front or Waterfall you spend 90% of the time designing and 10 % of the time coding. You then spend another 90% of the time handling all the changes that the initial design missed. And another 90% of the time chasing bugs.
With modern Agile development you spend 10% of the time designing and 90% of the time coding. then another 90% handling all the changes that the customer representative forgot to mention and another 90% of the time chasing bugs.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 7 years ago.
Improve this question
I've inherited a project where the class diagrams closely resemble a spider web on a plate of spaghetti. I've written about 300 unit tests in the past two months to give myself a safety net covering the main executable.
I have my library of agile development books within reach at any given moment:
Working Effectively with Legacy Code
Refactoring
Code Complete
Agile Principles Patterns and Practices in C#
etc.
The problem is everything I touch seems to break something else.
The UI classes have business logic and database code mixed in. There are mutual dependencies between a number of classes. There's a couple of god classes that break every time I change any of the other classes. There's also a mutant singleton/utility class with about half instance methods and half static methods (though ironically the static methods rely on the instance and the instance methods don't).
My predecessors even thought it would be clever to use all the datasets backwards. Every database update is sent directly to the db server as parameters in a stored procedure, then the datasets are manually refreshed so the UI will display the most recent changes.
I'm sometimes tempted to think they used some form of weak obfuscation for either job security or as a last farewell before handing the code over.
Is there any good resources for detangling this mess? The books I have are helpful but only seem to cover half the scenarios I'm running into.
It sounds like you're tackling it in the right way.
Test
Refactor
Test again
Unfortunately, this can be a slow and tedious process. There's really no substitute for digging in and understanding what the code is trying to accomplish.
One book that I can recommend (if you don't already have it filed under "etc.") is Refactoring to Patterns. It's geared towards people who are in your exact situation.
I'm working in a similar situation.
If it is not a small utility but a big enterprise project then it is:
a) too late to fix it
b) beyond the capabilities of a single person to attempt a)
c) can only be fixed by a complete rewriting of the stuff which is out of the question
Refactoring can in many cases be only attempted in your private time at your personal risk. If you don't get an explicit mandate to do it as part of you daily job then you're likely not even get any credit for it. May even be criticized for "pointlessly wasting time on something that has perfectly worked for a long time already".
Just continue hacking it the way it has been hacked before, receive your paycheck and so on. When you get completely frustrated or the system reaches the point of being non-hackable any further, find another job.
EDIT: Whenever I attempt to address the question of the true architecture and doing the things the right way I usually get LOL in my face directly from responsible managers who are saying something like "I don't give a damn about good architecture" (attempted translation from German). I have personally brought one very bad component to the point of non-hackability while of course having given advanced warnings months in advance. They then had to cancel some promised features to customers because it was not doable any longer. Noone touches it anymore...
I've worked this job before. I spent just over two years on a legacy beast that is very similar. It took two of us over a year just to stabilize everything (it's still broke, but it's better).
First thing -- get exception logging into the app if it doesn't exist already. We used FogBugz, and it took us about a month to get reporting integrated into our app; it wasn't perfect right away, but it was reporting errors automatically. It's usually pretty safe to implement try-catch blocks in all your events, and that will cover most of your errors.
From there fix the bugs that come in first. Then fight the small battles, especially those based on the bugs. If you fix a bug that unexpectedly affects something else, refactor that block so that it is decoupled from the rest of the code.
It will take some extreme measures to rewrite a big, critical-to-company-success application no matter how bad it is. Even you get permission to do so, you'll be spending too much time supporting the legacy application to make any progress on the rewrite anyway. If you do many small refactorings, eventually either the big ones won't be that big or you'll have really good foundation classes for your rewrite.
One thing to take away from this is that it is a great experience. It will be frustrating, but you will learn a lot.
I have (once) come across code that was so insanely tangled that I couldn't fix it with a functional duplicate in a reasonable amount of time. That was sort of a special case though, as it was a parser and I had no idea how many clients might be "using" some of the bugs it had. Rendering hundreds of "working" source files erroneous was not a good option.
Most of the time it is imminently doable, just daunting. Read through that refactoring book.
I generally start fixing bad code by moving things around a bit (without actually changing implementation code more than required) so that modules and classes are at least somewhat coherent.
When that is done, you can take your more coherent class and rewrite its guts to perform the exact same way, but this time with sensible code. This is the tricky part with management, as they generally don't like to hear that you are going to take weeks to code and debug something that will behave exactly the same (if all goes well).
During this process I guarantee you will discover tons of bugs, and outright design stupidities. It's OK to fix trivial bugs while recoding, but otherwise leave such things for later.
Once this is done with a couple of classes, you will start to see where things can be modularized better, designed better, etc. Plus it will be easier to make such changes without impacting unrelated things because the code is now more modular, and you probably know it thoroughly.
Mostly, that sounds pretty bad. But I don't understand this part:
My predecessors even thought it would
be clever to use all the datasets
backwards. Every database update is
sent directly to the db server as
parameters in a stored procedure, then
the datasets are manually refreshed so
the UI will display the most recent
changes.
That sounds pretty close to a way I frequently write things. What's wrong with this? What's the correct way?
If your refactorings are breaking code, particularly code that seems to be unrelated, then you're trying to do too much at a time.
I recommend a first-pass refactoring where all you do is ExtractMethod: the goal is simply to name each step in the code, without any attempts at consolidation whatsoever.
After that, think about breaking dependencies, replacing singletons, consolidation.
If your refactorings are breaking things, then it means you don't have adequate unit test coverage - as the unit tests should have broken first. I recommend you get better unit test coverage second, after getting exception logging into place.
I then recommend you do small refactorings first - Extract Method to break large methods into understandable pieces; Introduce Variable to remove some duplication within a method; maybe Introduce Parameter if you find duplication between the variables used by your callers and the callee.
And run the unit test suite after each refactoring or set of refactorings. I'd say run them all until you gain confidence about which tests will need to be rerun every time.
No book will be able to cover all possible scenarios. It also depends on what you'll be expected to do with the project and whether there is any kind of external specification.
If you'll only have to do occasional small changes, just do those and don't bother starting to refactor.
If there is a specification (or you can get someone to write it), consider a complete rewrite if it can be justified by the foreseeable amount of changes to the project
If "the implementation is the specification" and there are a lot of changes planned, then you're pretty much hosed. Write LOTS of unit tests and start refactoring in small steps.
Actually, unit tests are going to be invaluable no matter what you do (if you can write them to an interface that's not going to change much with refactorings or a rewrite, that is).
See blog post Anatomy of an Anti-Corruption Layer, Part 1 and Anatomy of an Anti-Corruption Layer, Part 2.
It cites Eric Evans, Domain-Driven Design: Tackling Complexity in the Heart of Software:
Access the crap behind a facade
You could extract and then refactor some part of it, to break the dependencies and isolate layers into different modules, libraries, assemblies, directories. Then you re-inject the cleaned parts in to the application with a strangler application strategy. Lather, rinse, repeat.
Good luck, that is the tough part of being a developer.
I think your approach is good, but you need to focus on delivering business value (number of unit tests is not a measure of business value, but it may give you an indication if you are on or off track). It's important to have identified the behaviors that need to be changed, prioritize, and focus on the top ones.
The other piece of advise is to remain humble. Realize that if you wrote something so large under real deadlines and someone else saw your code, they would probably have problems understanding it as well. There is a skill in writing clean code, and there is a more important skill in dealing with other people's code.
The last piece of advise is to try to leverage the rest of your team. Past members may know information about the system you can learn. Also, they may be able to help test behaviors. I know the ideal is to have automated tests, but if someone can help by verifying things for you manually consider getting their help.
I particularly like the diagram in Code Complete, in which you start with just legacy code, a rectangle of fuzzy grey texture. Then when you replace some of it, you have fuzzy grey at the bottom, solid white at the top, and a jagged line representing the interface between the two.
That is, everything is either 'nasty old stuff' or 'nice new stuff'. One side of the line or the other.
The line is jagged, because you're migrating different parts of the system at different rates.
As you work, the jagged line gradually descends, until you have more white than grey, and eventually just grey.
Of course, that doesn't make the specifics any easier for you. But it does give you a model you can use to monitor your progress. At any one time you should have a clear understanding of where the line is: which bits are new, which are old, and how the two sides communicate.
You might find the following post useful:
http://refactoringin.net/?p=36
As it is said in the post, don't discard a complete overwrite that easily. Also, if at all possible, try to replace whole layers or tiers with third-party solution like for example ORM for persistence or with new code. But most important of all, try to understand the logic (problem domain) behind the code.
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 10 years ago.
I was asked to do a code review and report on the feasibility of adding a new feature to one of our new products, one that I haven't personally worked on until now. I know it's easy to nitpick someone else's code, but I'd say it's in bad shape (while trying to be as objective as possible). Some highlights from my code review:
Abuse of threads: QueueUserWorkItem and threads in general are used a lot, and Thread-pool delegates have uninformative names such as PoolStart and PoolStart2. There is also a lack of proper synchronization between threads, in particular accessing UI objects on threads other than the UI thread.
Magic numbers and magic strings: Some Const's and Enum's are defined in the code, but much of the code relies on literal values.
Global variables: Many variables are declared global and may or may not be initialized depending on what code paths get followed and what order things occur in. This gets very confusing when the code is also jumping around between threads.
Compiler warnings: The main solution file contains 500+ warnings, and the total number is unknown to me. I got a warning from Visual Studio that it couldn't display any more warnings.
Half-finished classes: The code was worked on and added to here and there, and I think this led to people forgetting what they had done before, so there are a few seemingly half-finished classes and empty stubs.
Not Invented Here: The product duplicates functionality that already exists in common libraries used by other products, such as data access helpers, error logging helpers, and user interface helpers.
Separation of concerns: I think someone was holding the book upside down when they read about the typical "UI -> business layer -> data access layer" 3-tier architecture. In this codebase, the UI layer directly accesses the database, because the business layer is partially implemented but mostly ignored due to not being fleshed out fully enough, and the data access layer controls the UI layer. Most of the low-level database and network methods operate on a global reference to the main form, and directly show, hide, and modify the form. Where the rather thin business layer is actually used, it also tends to control the UI directly. Most of this lower-level code also uses MessageBox.Show to display error messages when an exception occurs, and most swallow the original exception. This of course makes it a bit more complicated to start writing units tests to verify the functionality of the program before attempting to refactor it.
I'm just scratching the surface here, but my question is simple enough: Would it make more sense to take the time to refactor the existing codebase, focusing on one issue at a time, or would you consider rewriting the entire thing from scratch?
EDIT: To clarify a bit, we do have the original requirements for the project, which is why starting over could be an option. Another way to phrase my question is: Can code ever reach a point where the cost of maintaining it would become greater than the cost of dumping it and starting over?
Without any offense intended, the decision to rewrite a codebase from scratch is a common, and serious management mistake newbie software developers make.
There are many disadvantages to be wary of.
Rewrites stop new features from being developed cold for months/years. Few, if any companies can afford to stand-still for this long.
Most development schedules are difficult to nail. This rewrite will be no exception. Amplify the previous point by, now, a delay in development.
Bugs that were fixed in the existing codebase through painful experience will be re-introduced. Joel Spolsky has more examples in this article.
Danger of falling victim to the Second-system effect -- in summary, ``People who have designed something only once before try to do all the things they "didn't get to do last time", loading the project up with all the things they put off while making version one, even if most of them should be put off in version two as well.''
Once this expensive, burdensome rewrite is completed, the very next team to inherit the new codebase is likely to use the same excuses for doing another rewrite. Programmers hate learning someone else's code. No one writes perfect code because perfection is so subjective. Find me any real-world application and I can give you a damning indictment and rationale for doing a from-scratch rewrite.
Whether you ultimately rewrite from scratch or not, beginning a refactoring phase now is a good way to both really sit down and understand the problem so that the rewrite will go more smoothly if truly called for, as well as giving the existing codebase an honest look to really see if a rewrite's needed.
To actually scrap and start over?
When the current code doesn't do what you would like it to do, and would be cost prohibitive to change.
I'm sure someone will now link Joel's article about Netscape throwing their code away and how it's oh-so-terrible and a huge mistake. I don't want to talk about it in detail, but if you do link that article, before you do so, consider this: the IE engine, the engine that allowed MS to release IE 4, 5, 5.5, and 6 in quick succession, the IE engine that totally destroyed Netscape... it was new. Trident was a new engine after they threw away the IE 3 engine because it didn't provide a suitable basis for their future development work. MS did that which Joel says you must never do, and it is because MS did so that they had a browser that allowed them to completely eclipse Netscape. So please... just meditate on that thought for a moment before you link Joel and say "oh you should never do it, it's a terrible idea".
A rule of thumb I've found useful is that if given a code base, if I have to re-write more than 25% of the code to make it work or modify it based upon new requirements, you may as well re-write it from scratch.
The reasoning is that you can only patch a body of code so far; beyond a certain point, it's quicker to do over.
There's an underlying assumption that you have a mechanism (such as thorough unit and/or system tests) that will tell you whether your re-written version is functionally equivalent (where it needs to be) as the original.
If it requires more time to read and understand the code (if that is even possible)
than it would to rewrite the entire application, I say scrap it and start over.
Be very carefull with this:
Are you sure you aren't just being lazy and not bothering to read the code
Are you being arrogant about the great code you will write compared to the rubbish anyone else produced.
Remember tested-working code is worth a lot more than imaginary yet-to-be-written code
In the words of our estemed host and overlord, Joel - things you should never do,
it's not always wrong to abandon working code - but you have to be sure about the reason.
I saw an application re-architected within 2 years of its introduction into production, and others rewritten in different technologies (one was C++ - now Java). Both efforts were were not, to my mind, successful.
I prefer a more evolutionary approach to bad software. If you can "componentize" your old app such that you can introduce your new requirements and interface with the old code, you can ease yourself into the new environment without having to "sell" the zero-value (from a biz perspective) investment in rewriting.
Suggested approach - write unit tests for the functionality with which you wish to interface to 1) ensure the code behaves as you expect and 2) provide a safety net for any refactoring that you may wish to do on the old base.
Bad code is the norm. I think IT gets a bad rap from business for favoring rewrites/rearchitecting/etc. They pay the money and "trust" us (as an industry) to deliver solid, extensible code. Sadly, business pressures frequently result in shortcuts that make the code unmaintainable. Sometimes it's bad programmers... sometimes bad situations.
To answer your rephrased question... can code maintenance costs ever exceed rewriting costs... the answer is clearly yes. I don't see anything in your examples, however, that lead me to believe this is your case. I think those issues can be addressed with tests and refactoring.
In terms of business value, I would think it's extremely rare that a real case can be made for a rewrite due solely to the internal state of the code. If the product's customer-facing and is currently live and bringing in money (i.e. is not a mothballed or unreleased product), then consider that:
You already have customers using it. They're familiar with it, and might have built some of their own assets around it. (Other systems that interface to it; products based on it; processes they'd have to change; staff they'd maybe have to retrain). All of this costs the customer money.
Re-writing it might cost less in the long term than making difficult changes and fixes. But you can't quantify that yet, unless your app is no more complex than Hello World. And a re-write means a re-test and a redeploy, and probably an upgrade path for your customers.
Who says the re-write will be any better? Can you honestly say your firm is writing sparkly code now? Have the practices that turned the original code to spaghetti been corrected? (Even if the main culprit was a single developer, where were his peers and management, ensuring quality through reviews, testing, etc.?)
In terms of technical reasons, I'd suggest it could be time for a major rewrite if the original has some technical dependencies that have become problematic. e.g. a third party dependency that's now out of support, etc.
In general though, I think the most sensible move is to refactor piece by piece (very small pieces if it's really that bad), and improve the internal architecture incrementally rather than in one big drop.
Two threads of thought on this one: Do you have the original requirements? Do you have confidence that the original requirements are accurate? What about test plans or unit tests? If you have those things in place it might be easier.
Putting on my customer hat, does the system work or is it unstable? If you've got something that's unstable you've got an argument to change; otherwise you're best of refactoring it bit by bit.
I think the line in the sand is when basic maintenance is taking 25% - 50% longer than it should. There comes a time when maintaining legacy code becomes too costly. A number of factors contribute to the final decision. Time and cost being the most important factors I think.
If there are clean interfaces and you can cleanly delineate module boundaries, then it might be worth refactoring it module by module or layer by layer in order to allow you to migrate existing customers forward into cleaner more stable codebases, and over time, after you've refactored every module, you will have rewritten everything.
But, based on the codereview, doesn't sound like there would be any clean boundaries.
I wonder if the people who vote for scrapping and starting over have ever successfully refactored a large project, or at least seen a large project in poor condition that they think could use a refactoring?
If anything, I err on the opposite side: I've seen 4 large projects that were a mess, that I advocated refactoring as opposed to rewriting. On a couple, there was barely a single line of original code that remained, and major interfaces changed in significant ways, but the process never involved the entire project failing to function as well as it originally did, for any more than a week. (And top-of-trunk was never broken).
Perhaps a project exists that is so severely broken that to attempt to refactor it would be doomed to failure, or perhaps one of the previous projects I refactored would have been better served by a "clean re-write", but I'm not sure I'd know how to recognize it.
I agree with Martin. You really need to weigh the effort that will be involved in writing the app from scratch against the current state of the app and how many people use it, do they like it, etc. Often we may want to completely start from scratch, but the cost far outweighs the benefit. I come across bits of ugly looking code all the time, but I soon realize that some of these 'ugly' areas are really bug fixes and make the program work correctly.
I would try to consider the architecture of the system and see whether it is possible to scrap and rewrite specific well defined components without starting everything from scratch.
What would usually happen is that you can either do that (and then sell that to the customer/management), or that you find out that the code is such a horrible and tangled mess that you become even more convinced that you need a rewrite and have more convincing arguments for it (including: "if we engineer it right, we would never need to scrap the whole thing and do a third rewrite).
Slow maintenance would eventually cause that architectural drift that would make a rewrite more expensive later.
Scrap old code early and often. When in doubt, throw it out. The hard part is convincing non-technical folks of the cost-to-maintain.
So long as the value derived appears to be greater than the cost to operate and maintain, there's still positive value flowing from the software. The question surrounding a rewrite this: "will we get even more value from a rewrite?" Or alternatively "How much more value will we get from a rewrite?" How many person-hours of maintenance will you save?
Remember, the rewrite investment is once only. The return on the rewrite investment lasts forever. Forever.
Focus the value question down to specific issues. You listed a bunch of them above. Stick with that.
"Will we get more value by reducing cost through
dropping the junk that we don't use
but still have to wade through?"
"Will we get more value from dropping the junk that's unreliable and breaks?"
"Will we get more value if we understand it -- not by documenting, but by replacing with something we built as a team?"
Do you homework. You'll have to confront the following show-stoppers.
These will originate somewhere in your executive foodchain from someone who'll respond as follows:
"Is it broken?" And when you say "It's not crashed as such," They'll say "It's not broke - don't fix it."
"You've done the code analysis, you understand it, you no longer need to fix it."
What's your answer to them?
That's only the first hurdle. Here's the worst possible situation. This doesn't always happen, but it does happen with alarming frequency.
Someone in your executive foodchain will have this thought:
"A rewrite doesn't create enough value. Rather than simply rewrite, let's expand it." The justification is that by creating enough value, users are more likely to buy in to the rewrite.
A project where scope is expanded -- artificially -- to add value is usually doomed.
Instead, do the smallest rewrite you can to replace the darn thing. Then expand to fit real needs and add value.
You can only give a definite yes to rewriting in case if you know completely how your application works (and by completely I mean it, not just having a general idea of how it should work) and you know more or less exactly how to make it better. Any other cases and it's a shot in the dark, it depends on too much things. Perhaps gradual refactoring would be safer if it is possible.
If possible, I typically would prefer to rewrite smaller portions of the code over time when I need to refactor a baseline. There are typically many smaller issues such as magic number, poor commenting, etc. that tend to make the code look worse than it actually is. So, unless the baseline is just awful, keep the code and just make improvements at the same time you are maintaining the code.
If refactoring requires a lot of work, I recommend laying out a small re-design plan/todo list that gives you a list of things to work on in order so that you can bring the baseline to a better state. Starting from scratch is always a risky move and you are not guaranteed that the code will be better when you are finished. Using this technique, you will always have a working system that improves over time.
Code with excessively high cyclomatic complexity (like over 100 in a large number of modules) is a good clue. Also, how many bugs does it have / KLOC? How critical are the bugs? How often are bugs introduced when bug fixes are made. If your answer is a lot (I cant remember norms right now), then a rewrite is warranted.
As early as possible. Whenever you get a premonition that your code is slowly turning into an ugly beast that is very likely to consume your soul and give you headaches, and you know the problem is in the underlying structure of the code (so any fix would be a hack, e.g. introduce a global variable), then it's time to start over.
For some reasons people don't like throwing away precious code, but if you feel your better off starting over, you are probably right. Trust your instinct and remember that it wasn't a waste of time, it taught you one more way of NOT approaching the problem. You could (should) always use a version control system so your baby is never really lost.
I do not have any experience with using metrics for this myself, but the
article
"Software Maintainability Metrics Models in Practice" discusses
more or less the same question asked here for two case studies they did.
It starts with the following editor's note:
In the past, when a maintainer
received new code to maintain, the
rule-of-thumb was "If you have to
change more than 40 percent of someone
else's code, you throw it out and
start over." The Maintainability Index
[MI] addressed here gives a much more
quantifiable method to determine when
to "throw it out and start over." This
work was sponsored by the U.S. Air
Force Information Warfare Center and
the U.S. Department of Energy [DOE],
Idaho Field Office, DOE Contract No.
DE-AC07-94ID13223.)
I think the rule was...
The first version is always a throw away
So, if you learned your lesson(s), or his/her lessons, then you can go ahead and write it fresh now that you understand your problem domain better.
Not that there aren't parts that can/should be kept. Tested code is the most valuable code, so if it isn't deficient in any real way other than style, no reason to toss it all out.
When is it good (if ever) to scrap production code and start over?
Never had to do this, but logic would dictate (to me, anyway) that once you pass the inflection point where you're spending more time reworking and fixing bugs in the existing code base than you are adding new functionality, it's time to trash the old stuff and get a fresh start.
If it requires more time to read and understand the code (if that is even possible) than it would to rewrite the entire application, I say scrap it and start over.
I have never completely thrown out code. Even when going from a foxpro system to a c# system.
If the old system worked then why just throw it out?
I have come across a few really bad system. Threads being used where not needed. Horrible inheritance and abuse of interfaces.
It is best to understand what the old code is doing and why it is doing it. Then change it so that it is not confusing.
Of course if the old code doesn't work. I mean can't even compile. Then you might be justified in just starting over. But how often does that actually happen?
Yes, it totally can happen. I've seen money be saved by doing it.
This is not a tech decision, it's a business decision. Code rewrites are long term gains, while "if it ain't totally broke..." is a short term gain. If you are in a first year startup that is focused on getting a product out the door, the answer is usually to just live with it. If you're in an established company, or the errors with the current systems are causing more workload, therefor more company money.. then they might go for it.
Present the problem as best as you can to your GM, use dollar values where you can. "I don't like dealing with it" means nothing. "It'll take twice the time to do everything until this is fixed" means a lot.
I think there are a number of issues here that depend largely on where you are at.
Is the software working well from a customer perspective? (If yes be very careful about changes). I would think there would be little point re-witting unless you were expanding the feature set if the system was working. And are you planning to expand the features and customer base of the software? If so then you have much more reason to change.
As much as anything just trying to understand some else's code even if well written can be difficult, when badly written I would imagine almost impossible. What you describe sounds like something that would be very difficult to expand.
I would take into consideration if the application does what it is intended to do, is required for you to ever make modifications, and are you confident that the app has been thoroughly tested in all scenarios that it will be used in.
Do not invest the time if the app does not need alterations. However, if it doesn't function as you need and you need to control the hours and time invested to make corrections, scrap it and re-write to the standards that your team can support. There's nothing worse than terrible code that you have to support / decipher but still have to live with. Remember, Murphy's Law says it will 10 at night when you'll have to make things work, and that is never productive.
Production code always has some value. The only case where I would truly throw it all out and start again is if we determine the intellectual property is irrevocably contaminated. For example if someone brought large amounts of code from a previous employer, or a large percentage of the code was ripped from a GPLd codebase.
I'm going to post this book every time I see a discussion on Refactoring. Everyone should read "Working Effectively with Legacy Code" by Michael Feathers. I found it to be an excellent book - if nothing else, it's a fun read, and motivational.
When the code has reached a point that is not maintainable or extensible anymore. Is full of short-term hacky fixes. It has lots of coupling. It has long (100+lines) methods. It has database access in the UI. It generates a lot of random, impossible to debug errors.
Bottom line: When maintaining it is more expensive (i.e. takes longer) than rewriting it.
I used to believe in just re-write from scratch, but it is wrong.
http://www.joelonsoftware.com/articles/fog0000000069.html
Changed my mind.
What I would suggested is figuring out a way to properly refactor the code. Keep all existing functionality and test as you go. We have all seen horrible code bases, but it is important to keep the knowledge over time you application has.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 years ago.
Improve this question
When coding, what in your experience is a better approach?
Break the problem down into small enough pieces and then implement each piece.
Break the problem down, but then implement using a top-down approach.
Any other?
I tend to design top-down and implement bottom-up.
For implementation, building the smallest functional pieces and assembling them into the higher-level structures seems to be what works best for me. But, for design, I need to start from the overall picture and break it down to determine what those pieces will be.
Here's what I do:
Understand the domain first. Understand the problem to be solved. Make sure you and the customer (even if that customer is you!) are on the same page as to what problem is to be solved.
Then a high level solution is proposed to the problem and from that, the design will turn into bubbles or bullets on a page or whatever, but the point is that it will shake out into components that can be designed.
At that point, I write tests for the yet-to-be written classes and then flesh out the classes to pass those tests.
I use a test-first approach and build working, tested components. That is what works for me. When the component interfaces are known and the 'rules' are known for how they talk to each other and provide services to each other, then it becomes generally a straightforward 'hook everything together' exercise.
That's how I do it, and it has worked well for me.
You might want to look over the Agile Manifesto. Top down and bottom up are predicated on Built It All At Once design and construction.
The "Working software over comprehensive documentation" means the first thing you build is the smallest useful thing you can get running. Top? Bottom? Neither.
When I was younger, I worked on projects that were -- by contract -- strictly top down. This doesn't work. Indeed, it can't work. You get mountains of redundant design and code as a result. It was not a sound approach when applied mindlessly.
What I've noticed is that the Agile approach -- small pieces that work -- tends to break the problem down to parts that can be grasped all at once. The top-down/bottom-up no longer matters as much. Indeed, it may not matter at all.
Which leads do: "How do you decompose for Agile development?" The trick is to avoid creating A Big Thing that you must then decompose. If you analyze a problem, you find actors trying to accomplish use cases and failing because they don't have all the information, or they don't have it in time, or they can't execute their decisions, or something like that.
Often, these aren't Big Things that need decomposition. When they are, you need to work through the problem in the Goals Backward direction. From Goals to things that enable you to make that goal to things that enable the enablers, etc. Since goals are often Big Things, this tends to be Top Down -- from general business goal to detailed business process and step.
At some point, we overview these various steps that lead to the goals. We've done the analysis part (breaking things down). Now comes the synthesis part: we reassemble what we have into things we can actually build. Synthesis is Bottom Up. However, let's not get carried away. We have several points of view, each of which is different.
We have a model. This is often built from details into a larger conceptual model. Then, sometimes decomposed again into a model normalized for OLTP. Or decomposed into a star schema normalized for OLAP. Then we work back up to create a ORM mapping from the normalized model. Up - Down - Up.
We have processing. This is often built from summaries of the business processes down into details of processing steps. Then software is designed around the steps. Then the software is broken into classes and methods. Down - Up - Down.
[Digression. With enlightened users, this decomposition defines new job titles and ways of working. With unenlightened users, the old jobs stay and we write mountains of documentation to map old jobs onto new software.]
We have components. We often look at the pieces, look at what we know about available components, and do a kind of matching. This is the randomest process; it's akin to the way crystals form -- there are centers of nucleation and the design kind of solidifies around those centers. Web Services. Database. Transaction Management. Performance. Volume. Different features that somehow help us pick components that implement some or all of our solution. Often feels bottom-up (from feature to product), but sometimes top-down ("I'm holding a hammer, call everything a nail" == use the RDBMS for everything.)
Eventually we have to code. This is bottom up. Kind of. You have to define a package structure. You have to define classes as a whole. That part was top down. You have to write methods within the classes. I often do this bottom-up -- rough out the method, write a unit test, finish the method. Rough out the next method, write a unit test, finish the method.
The driving principle is Agile -- build something that works. The details are all over the map -- up, down, front, back, data, process, actor, subject area, business value.
Yes. Do all of those things.
It may seem sarcastic (sorry, I revert to form), but this really is a case where there is no right answer.
Also in the agile way, write your test(s) first!
Then all software is a continual cycle of
Red - the code fails the test
Green - the code passes the test
Refactor - code improvements that are intention-preserving.
defects, new features, changes. It all follows the same pattern.
Your 2nd option is a reasonable way to go. If you break the problem down into understandable chunks, the top down approach will reveal any major design flaws before you implement all the little details. You can write stubs for lower level functionality to keep everything hanging together.
I think there's more to consider than top- verses bottom-down design. You obviously need to break the design up into manageable units of work but you also need to consider prioritisation etc. And in an iterative development project, you will often redefine the problem for the next iteration once you've delivered the solution for the previous one.
When designing, I like to do middle-out. I like to model the domain, then design out the classes, move to the database and UI from there. If there are specific features that are UI-based or database-based, I may design those up front as well.
When coding, I generally like to do bottom-up (database first, then business entities, then UI) if at all possible. I find it is a lot easier to keep things straight with this method.
I believe that with good software designers (and in my opinion all software developers should also be software designers at some level), the magic is in being able to do top-down and bottom-up simultaneously.
What I was "schooled" to do by my mentors is start by very brief top-down to understand the entities involved, then move to bottom-up to figure out the basic elements I want to create, then to back up and see how I can go one level down, knowing what I know about the results of my bottom up, and so forth until "they meet in the middle".
Hope that helps.
Outside-in design.
You start with what you're trying to achieve at the top end, and you know what you've got to work with at the bottom end. Keep working both ends until they meet in the middle.
I sort of agree with all of the people saying "neither", but everyone falls somewhere on the spectrum.
I'm more of a top-down kind of guy. I pick one high level feature/point/whatever and implement it as a complete program. This lets me sketch out a basic plan and structure within the confines of the problem domain.
Then I start with another feature and refactor out everything from the original that can be used by the second into new, shared entities. Lather, rinse, repeat until application is complete.
However, I know a lot of people who are bottom up guys, who hear a problem and start thinking about all the support subsystems that they could need to build the application on top of it.
I don't believe either approach is wrong or right. They both can achieve results. I even try and find bottom up guys to work with, as we can attack the problem from two different perspectives.
Both are valid approaches. Sometimes one just "feels" more natural than the other. However, there is one big problem: some mainstream languages and especially their frameworks and libraries really heavily on IDE support, such as syntax highlighting, background type checking, background compilation, intelligent code completion, IntelliSense and so on.
However, this doesn't work with top-down coding! In top-down coding, you constantly use variables, fields, constants, functions, procedures, methods, classes, modules, traits, mixins, aspects, packages and types that you haven't implemented yet! So, the IDE will constantly yell at you because of compile errors, there will be red squiggly lines everywhere, you will get no code completion and so on. So, the IDE pretty much prohibits you from doing top-down coding.
I do a variant of top-down. I tend to try and do the interface first - I then use that as my list of features. What's good about this version is, it still works with IDE that would otherwise complain. Just comment out the one function call to what's not yet been implemented.