Sign in to follow this  

Stable code

This topic is 2635 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts


Hello, I have been programming for about 2 years now.
Mostly in c plus plus.
Been working in the win32 and direct-x apis doing both 2D and 3D applications.

I consider myself to have the basic knowledge, but as they say, "the more you know the more you realize how little you know".

I would like to get some clarifications on what stable code is.

Any examples would be great.

Thanks in advance :)

Share this post


Link to post
Share on other sites
Quote:

if(values.size() > i)
{
return values[i];
}


Definitely a step in the right direction, but writing stable code is about thinking of EVERY possible way the code could go wrong and making sure it no longer can.

It's a mindset you will get yourself into after a while of practice. Go through your code you already have and imagine what would happen if input in your functions was not as expected. Think of the entire range of a given data type when you see it coming into a function. For example, integers can be negative, so:


if ( values.size() > i && i >= 0 )
{
return values[i];
}

Share this post


Link to post
Share on other sites
in addition to making sure you handle unexpected input well, handling your memory correctly will also go along ways into making sure you code is stable,

nulling pointers when they are first declared,
anytime a pointer is deleted nulling it
and possibly implementing some form of garbage collection or reference counting can go along ways to making your code stable

one of the things that has helped me the most with making sure my code is stable, using readable/understandable variable and function names

Share this post


Link to post
Share on other sites
Quote:
Original post by empirical2
AFAIK Stable code is simply code that:
1. Is bug free or as close as possible.
This. Stable code should be well tested, and unlikely to need change in the near future.
Quote:
2. Is not breakable by invalid input or abnormal or unexpected situations.
This would be more along the lines of 'robust' or 'secure' code. It isn't generally expected that "stable" code be bullet-proof in the face of arbitrary/malicious invalid input.

Share this post


Link to post
Share on other sites
Quote:
Original post by swiftcoder
This would be more along the lines of 'robust' or 'secure' code. It isn't generally expected that "stable" code be bullet-proof in the face of arbitrary/malicious invalid input.


Well personally I would not distinguish between stable and robust. If code is going to fall over because the user does something stupid (100% dead cert) I do not consider it to be stable. However, I'm not a professional and if 'stable' has a defined specific meaning, I, of course, accept that.

Share this post


Link to post
Share on other sites
If your coding something and your not sure if its gonna crash or not, avoid using while loops, cause they can infintesimally cycle if their break condition is never met.
Something that needs to be coded securely and robustly for example is CSG (constructive solid geometry) its when you get a ball and cube and take a chunk out of the cube with the ball or vice versa.

The code usually gets to be 1000 lines long (longer if you stuff it up) and theres many points inside that can stuff up if the piece of code before doesnt output correctly.

Tesselation is another algorythm which should be robust and allow invalid inputs and still work.

CSG and EAR CLIPPING are 2 algorythms that both best remind me to be very secure when I code, being less lazy can sometimes be less work than careless
programming that ends up getting you no-where.

Youll be coding these 2 algorythms when you are making a model editor or environment builder.

They are both in the "geometry tool" class of algorythms, and in this class of algorythms I say think it 4 times over! before you put hand to keyboard.

Even if you cant see how to get an algorythm to work with every single case you can think of... you can at least let it "fail gracefully" and not crash the program terminally.

Share this post


Link to post
Share on other sites
I think stable code is code that is (close to) unbreakable by user input and varying factors (time, thread scheduling, etc). On the other hand, I do *not* think that writing code in a way that it silently ignores bad input from the programmer is a good idea. Consider this silly example:


void addPlayerToWorld(Player* player, const Vector3* spawnPoint)
{
// Must not be null.
if(spawnPoint == NULL)
return;
player->setPosition(spawnPoint);
players.push_back(player);
}




If someone, for whatever stupid reason, assumes that NULL is a valid value for the second parameter, the program will not crash. Instead, it will silently ignore the error and do nothing, leaving the programmer confused as to why the desired result isn't achieved. A better way to handle this might be with a debug assertion, so that the programmer can clearly see what went wrong, and where, without affecting production code.

Share this post


Link to post
Share on other sites
Quote:
Original post by empirical2
Well personally I would not distinguish between stable and robust. If code is going to fall over because the user does something stupid (100% dead cert) I do not consider it to be stable.
The difference is in the potential cost of failure, and whether it justifies the increased cost of development.

There are fields (airport traffic control, nuclear reactor control, medical equipment, etc.) where robustness is not only desirable but required. In these fields, even a single error among millions of lines of code can cost people their lives, and thus you spend an incredible amount of time/money/effort in making sure that no error can ever occur, and an even larger amount on making sure that when it does occur, you can handle it gracefully.

For normal software (applications, games, etc.), we don't need to handle every eventuality robustly. There are many cases where it is acceptable to just crash, for example: power-outages, hard-drive crashes, a dying memory chip returning random values, some random idiot manually corrupting your stack with a debugger, ...

Share this post


Link to post
Share on other sites
Quote:
Original post by swiftcoder
For normal software (applications, games, etc.), we don't need to handle every eventuality robustly. There are many cases where it is acceptable to just crash, for example: power-outages, hard-drive crashes, a dying memory chip returning random values, some random idiot manually corrupting your stack with a debugger, ...


No but then, thats not at all what I meant by robust. Or by the user doing something stupid. I meant mishaps in day to day operations.

Example of what I did mean by user stupidity:

User clicks the delete button without first selecting an item. Stable (perhaps would be called robust) would be "No, you must select an item first" not "Unhandled exception".

Example of what I did not mean by user stupidity:

User open case and goes at the RAM with a hammer and chisel.

Dealing with mistakes a user is likely to make would, [u]to me[/u], be a part of code stability.

But if code stability means something else in a professional environment, fair enough.

Share this post


Link to post
Share on other sites
To make this clear, "stable" and "robust" are different but overlapping qualities of code.

Stable means: The code (that could mean interface or implementation) is unlikely to change, well tested (that follows from the former, since only well tested code needs no more changes), in one word: reliable.[1]

Robustness is one quality of code among others that make software stable - as in, no more bugfixes.

[1] "A live release is considered to be very stable and relatively bug-free with a quality suitable for wide distribution and use by end users."

Share this post


Link to post
Share on other sites
I'm a little surprised I haven't seen it above, so throwing it in.



Use assert.

The built-in assert macros are a start, but a full-blown logging assertion system is a must for large projects. It is useful for assertions to make log entries, and for the system to give a way to silence future instances of a specific assertion.

Assert that your inputs are valid. Assert that your results are sane. Whenever you discover someone called a function with invalid data, add an assertion that would capture it in the future.

Every assertion should have both the assert (seen by programmers and testers by hitting a breakpoint, logging and/or showing some UI) and the corrective action that is taken (action when assert is skipped and on shipping builds).

Assertions cause the debugger to kick in so programmers can find the issue in the call stack, and a good assertion library will let QA and testers grab useful information. They are also awesome because they are compiled out of final builds so there is zero cost to the customer.



One feature of a mature code base is that the code is covered with asserts and each one of them has a corrective action.

Share this post


Link to post
Share on other sites
Quote:
Original post by frob
One feature of a mature code base is that the code is covered with asserts and each one of them has a corrective action.
Or for a more formal specification of the same, Design by Contract, primarily with respect to languages which support contract programming natively, such as D.

Share this post


Link to post
Share on other sites
Quote:
Original post by PrestoChung
Noob question: Is this assert thing like exception handling?

No.

You error-check stuff that could go wrong. The objective of error checking is to correct the problem, or gracefully tell the user you had a problem.

Exceptions are similar but handle errors that shouldn't be happening normally.

You assert things that can't go wrong. Asserts are for sanity checking. It keeps a function from being used incorrectly, and stops in the debugger when it is used incorrectly. They should never get hit, but if they ever do you know their is a bug in the code.


// Reading a file. Anything could go wrong.
// Same with user input. Network data. etc.
std::ifstream file("my.txt");
int data;
if( !( file >> data ) )
{
std::cerr << "ERROR! CAN"T READ HEADER!!!" << std::endl;
}

vs

// Allocating memory, it should almost always just work
// but we could have fragmented too much or some other edge case.
try
{
foo = new foo[200000];
}
catch(...)
{
// whoops! out of memory!
}

vs

// Sanity check inputs! The function has error correction anyway, but
// a dev will hit the assert in the debugger. This gives easy access
// to the problem without having to debug as much. The asserts aren't errors or
// exceptions because this function isn't EVER supposed to be called with bad
// data. If the data is bad, the caller screwed up something or someone overwrote
// the caller's data with something bad.
void DoStuff( unsigned char *buffer, const size_t sz, const foo &object )
{
assert( buffer );
assert( sz < MAX_BUFFER_SZ );

if( !buffer || sz >= MAX_BUFFER_SZ )
return;

assert( object.is_valid() );
assert( object.is_active() );
if( !object.is_valid() || !object.is_active() )
return;

......
}



(Windryder's example is probable better, and less made up...)
edit:
Quote:

I would go about:

if(values.size() > i)
{
return values[i];
}

The "at" member of a std::vector does those range checks for you, ie. values.at(i);. They are asserts as well, and can be (usually are?) disabled in release.

Share this post


Link to post
Share on other sites
Quote:
Original post by Windryder
I think stable code is code that is (close to) unbreakable by user input and varying factors (time, thread scheduling, etc). On the other hand, I do *not* think that writing code in a way that it silently ignores bad input from the programmer is a good idea. Consider this silly example:


void addPlayerToWorld(Player* player, const Vector3* spawnPoint)
{
// Must not be null.
if(spawnPoint == NULL)
return;
player->setPosition(spawnPoint);
players.push_back(player);
}






Quote:
Original post by Windryder
If someone, for whatever stupid reason, assumes that NULL is a valid value for the second parameter, the program will not crash. Instead, it will silently ignore the error and do nothing, leaving the programmer confused as to why the desired result isn't achieved. A better way to handle this might be with a debug assertion, so that the programmer can clearly see what went wrong, and where, without affecting production code.



No, that sucks. A better way would be using a reference.

Share this post


Link to post
Share on other sites
I strongly recommend this article, which was published in Communications last year:
http://sdg.csail.mit.edu/pubs/2009/dnj-cacm-full.pdf
While it tends to be targeted for critical and semi-critical applications, which games are not examples of, the underlying concept is a very useful one for any software developer: that one should directly be able to argue from the design and implementation (in away that leaves no doubt in the verifier who is reading the 'proof') that certain behaviors of the overall system are unconditionally met. The emphasis on the whole system forces consideration of interactions and side-effects, which may be incorrectly assumed to be not an issue if one is just doing unit tests, for example; at the same time, in order to make a detailed argument of the safety of this code tractable, there is additional constraint of minimizing coupling and side-effects, which I'd say is a good thing. But I can't do the article justice here, so just read it.

In the meantime, formal verification by automatic checkers is slowly making advances and correctness proofs of complex real-world programs will be a reality in a decade or two.

Share this post


Link to post
Share on other sites
I think nobody has explicitly mentioned the importance of checking the return values of all the system calls you make. This is especially important when you are writing networking code.

About the difference between "robust" and "stable", the way I use the terms, "robust" means the code won't break if it receives stupid or malicious input, and it handles all exceptional conditions gracefully (at least providing a meaningful error message). "Stable" means it doesn't change much over time. Code that is not robust often needs to be patched as problems are discovered, so it tends to not be stable either.

Share this post


Link to post
Share on other sites
Just be thankful you don't need to program according to the "Type C" standard that our new electric fence controllers are written to. There, not only does your code need to be pretty much bulletproof, but you also are not allowed to trust the processor that your code is written on. Every calculation result has to be independently verified by a different operation etc.
I'm sure they do the same kind of thing for the space shuttle too.

Share this post


Link to post
Share on other sites

This topic is 2635 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this