This is an article about maintaining your code once it's written. By this point, your project is useful. People (possibly you) are using it to actually do some science. However, there's more still to be done and it would be a very bad plan to ignore this part.
The need for maintenance
Fixing bugs never really ends (sadly). You can never be absolutely, 100% certain that there are no bugs remaining in your code. Indeed, you can generally be very confident that there are at least some small ones still there. The best you can generally hope for is that the number of significant bugs tails away towards zero.
Happily, with ongoing maintenance, this is often a reasonable thing to aim for. If your code is well conceived, planned, written and tested (which it is, because you've been following our advice), then you should be in a good position to fix bugs as you and your users find them. And after a while of this, you'll find that the number of new bugs drops off until everyone is using the latest release of your program and finds it extremely reliable.
As with many other aspects of this whole process, the discovery of new bugs is a Good Thing. It means people are using your code and (presumably) finding it useful. Each bug that's fixed is one fewer that the users have to worry about. And if you're also a user, this is even better because you get to benefit directly from the maintenance by having improved software with which to do your science.
Maintenance is also necessary sometimes because things in your program need tweaking, perhaps to improve usability or improve how an interface displays results. These aren't bugs to be fixed, but are nevertheless important improvements. If you can provide the user with a tool that's easy and intuitive to use, they'll thank you for it!
There's a nice Japanese word for this, "Kaizen", which means continuous, incremental improvements. Companies like Toyota use it as a guiding principle and it's certainly also highly appropriate to producing high quality code. Each improvement is small and doesn't take much time to make, but the aggregate effect over time is that you get a much better product.
Including new features
Sometimes, you (or one of your users) will come up with good ideas for new features. This is great, because you can all benefit from this, but it does need a bit of care. Remember, one of the important parts of planning was to get all the objectives sorted out before writing code because it's a lot easier that way. We're now talking about changing the objectives not only after the code's written, but after it's tested as well, which means it'll need re-testing.
This is a type of feature creep. This isn't necessarily a bad thing, because you stand to gain a good new feature in your program. But there are also downsides that you need to consider before you commit to making the changes necessary to include the new feature.
Firstly, your plan will no longer be accurate. This means that the previously-optimal design/layout/structure may no longer be. If your design is flexible, it may be able to cope with this change. What usually happens is the code becomes less flexible as a result, which can be fine or can result in code becoming hard/impossible to work with.
Secondly, you will almost certainly introduce new bugs when you change the code. This is kind of inevitable (think of it as code entropy that's introduced as you change things around). This means you'll have to put effort into additional testing and debugging, and probably re-run your previous tests to make sure you haven't broken anything.
Thirdly, your changes may have unexpected consequences. For example, a variable that was previously always constant may now change and your original code may crash as a result. This is one reason why splitting your code into chunks is so important - it makes this kind of hidden interdependence a lot less likely. But nevertheless, introducing a new feature, particularly one not in the original plan, can have knock-on effects so you need to be careful.
The way to handle this is to think about the cost-benefit balance. If the new feature would be very useful and only requires a small change to very flexible code, then it may be a very good idea. If the change requires re-writing all the underlying data structures and how they're handled, but will only give a small, incremental improvement, then maybe you and your users should make do without the change. Make a note of the idea and move on.
Onwards to version 2.0
Eventually, you may accumulate quite a large number of improvements you'd like to make. There maybe a range of new features you'd like to incorporate. You might have some great ideas about how you can refactor your code to make it more efficient and easy-to-handle. Either way, you feel the changes are too large for an incremental improvement. In this case, you might consider writing version 2.0 of your program.
Version 2.0 essentially involves going back to the conceptual objectives (or even programming-language-decision) stage and re-developing the project from there. This can be a major re-built, but means you can properly incorporate all the new experience, good ideas and understanding you have from your efforts up until now. It will likely be a lot of effort, so don't do this lightly, but if you do it well then version 2.0 should be a major upgrade over your original program, good though that was.
Maintenance is about honing and fine-tuning your software. There's no substitute for the rigorous testing that comes from having your program used on a wide range of problems, possibly by a wide range of people. And each incremental improvement you make directly helps out the science you and others generate using your software. You may even accumulate enough ideas for new features and improvements that you decide to write a version 2.0, which will be even better.