Followers 0

# How often do you revisit your hacky solutions or "ToFix" comments?

## 27 posts in this topic

I was just looking at some (working) code that i wrote a couple of years ago that's buried deep in my rendering engine. There was quite a few of these "solutions" that i completely forgot about. I found it pretty funny that i wrote before a particular hacky solution: "Dirty hack... fix later".

0

##### Share on other sites

I have a kind of a todo list in my tiny slowly progressing game project, with a section for things that i should go through when something falls apart for no apparent reason.

0

##### Share on other sites

I am afraid to type TODO in visual studio find popup.. Since its a hobby project for learning purpose, I only go back when things break.

Im doing an engine and a game, so while the game is running I dont mess with the engine, unless its just to add new stuff that I need and the engine doesnt provide.

Means the TODO comments only grows. Im telling to myself that when the game is over I will go throu all the problems I already identified and redo everything in a better way. I dont do this now cause the game is running fine, and I dont want to take a side quest with the engine and make the game break till the quest is complete.

Edited by Icebone1000
0

##### Share on other sites

Whenever necessary. If the "hack" gives the desired results and is just ugly for style reasons or wastes a few cycles, I don't bother with it usually. If it breaks the program, it obviously has to be fixed (with another hack )

It's different if the "hack" has influence on your program and leads to writing more hackish code (like using a global as a shortcut and then start using it throughout the program). Those should be fixed asap.

0

##### Share on other sites

Ehmm... To be fair, so far in my latest projects, there is only one hack that comes to mind that I haven't fixed (river particle stopping code) because I'm working with the UI now.

I will come to it because the sim needs to be right for everything to look remotely good.

Now, on the UI code? I'm on the point that I'm not sure if I am writing hackish solutions or not. Funnily enough I can find out about that by saying "What if I changed Swing GUI for a SWT based one?" and then all the dependencies/fckups become clear.

0

##### Share on other sites

If it's a hack, it isn't ready to commit.

0

##### Share on other sites

If it's a really bad hack, I might fix it that same day or the next day, as soon as I get the hacky version functional (because once it's functional, sometimes I can see a cleaner way of doing it that I couldn't previously).

If it's a minor bit of sloppy code, and I really don't want to work on it today, I'll comment it as hacky, and later if I need to expand that function, then I'll usually rewrite the entire function with the new features and cleaner than before.

On very very rare occasions, the 'hack' is the cleanest way of doing something. I only have one of these in my current code base - a const_cast() of a string used to write to its internal buffer:

void Serialize(Serializer &serializer, std::string &value)
{
//Serialize the string size.
uint16_t size = value.size();
Serialize(serializer, size);
value.resize(size);

//Serialize the characters.
char *cStr = const_cast<char*>(value.data()); //DANGER: Hack. I'm using the .data() function of std::string to write to std::string's internal buffer.
serializer.Serialize(reinterpret_cast<Byte*>(cStr), size);
} 

Potentially very dangerous, but almost certainly workable in major implementations of the standard library - unless the implementation decides to copy its internal buffer at every call to .data(), or unless it has a duplicate buffer for some reason, .data() should return the internal char* buffer of the std::string.

That's clean code but hacky code. I have messier code in several locations, but I don't have anything hackier than that, iirc.

0

##### Share on other sites

I put a //TODO: *whatever there is to do* at that place in the code and I periodically Ctrl + f //TODO:  to find the places where I have something to fix and as a rule I never commit if a single //TODO exists in the code.That way I'm compelled to fix them as soon as I can.Also I've made it a habit to recognize places where things have a tendency to become "tricky", basically the places that will be responsible for a lack of flexibility and the need to use some not so good method/hack and I spend extra care planning the big picture on these places, so I'm sure that everything will go as planned.I used to use a lot more hacks in the past when I had the habit of thinking as I code, but when I started to actually plan my stuff on paper, the tendency to use hacks reduced greatly.

0

##### Share on other sites

Most programmers abhor unclean code and will naturally fix hacks if given the time (see 20% time).

But honestly, it depends on the hack. There are levels of hack from "ugh, slightly dodgy downcast here" to "mondo hackitude o rama" (thanks Microsoft  ).

It also depends on the program. If it's a library that will be used by millions of users, fix the damn hack. If it's your own little project, well, it's your time... fix it or not at your discretion.

But if it's in a program that you are being paid to write, you have a financial responsibility to your employer. Do a cost benefit analysis.

• How long will it take to fix?
• How will the product be better if I fix this?
• Will it be faster?
• more stable?
• will it ease future development?
• or more importantly, what are the risks of not fixing it?
• Can the hack cause a crash?
• or worse still.. data loss? Users will forgive a crash, but losing work (a saved game for instance) will really annoy them.

At the end of the day, you are paid to make sensible decisions about how to use your time.

Pragmatism trumps idealism every time.

Edited by ChaosEngine
0

##### Share on other sites

My code generally [i]starts[/i] as a list of 'ToDo:' comments, often before the project even gets a proper name.

So, 'most' of my ToDo comments get followed up on, and I can easily reach a 90+% 'finished' on the ToDo list for any given project, even if less than 20% functionality has been implemented. (Some of those ToDo entries are to create other ToDo entries that are needed to flesh out the feature's requirements.)

It might not be right, but it looks impressive if you put it all into a nice big chart.

0

##### Share on other sites

Most programmers abhor unclean code and will naturally fix hacks if given the time (see 20% time).

But honestly, it depends on the hack. There are levels of hack from "ugh, slightly dodgy downcast here" to "mondo hackitude o rama" (thanks Microsoft  ).

Didnt Bill Gates said he prefer lazy ppl because they find ways to do the work faster and easier?

0

##### Share on other sites

Most programmers abhor unclean code and will naturally fix hacks if given the time (see 20% time).

But honestly, it depends on the hack. There are levels of hack from "ugh, slightly dodgy downcast here" to "mondo hackitude o rama" (thanks Microsoft  ).

Didnt Bill Gates said he prefer lazy ppl because they find ways to do the work faster and easier?

Dunno if Gates said that, but I'd agree with it.

To paraphrase Terry Pratchett "it takes a lot of work to be truly lazy".

0

##### Share on other sites

I usually put down //TODO: and //POSSIBLE TODO: in my code. The former is generally something I will revisit as it's part of developing the functionality I've been assigned to do - I just need my brain to focus somewhere else for a moment. The //POSSIBLE TODO: category (that I might sometimes abbreviate to //TODO: stupidly enough) are things that at some undefined point in the future might become relevant to change. I rarely end up revisiting these.

Also have //HACK: and //NOTE: tags to warn for and explain WTF-code. Not using these nearly as often, for obvious reasons.

Edited by StubbornDuck
0

##### Share on other sites

The only time I commit hacks is when we're up against a deadline, the fix is localized and the proper fix requires tons of testing to a bunch of systems. I generally have a proper fixed shelved in perforce for when the game is safe to commit these changes to again.

I often find myself fixing a lot of other engineers' TODO's. Often when tracking down bugs I'll debug down to the problem only to find a comment stating: "TODO: you need to do blah or it will break when blah happens"

I do sometimes leave TODO comments when I want to revisit an existing implementation later on when I have more time, but these aren't usually hacks, just not extensible in the way I'

0

##### Share on other sites

When the number of "todo" comments crosses some arbitrary threshold - which varies in space-time, aka the "I gotta fix this mess" effect - I focus on getting them down to a sane level before continuing to add more features, if I have time (e.g. no imminent deadline or priority feature to implement). I eventually get around to it for projects I really care about. Eventually.

Well, it depends, of course. If it's things like "todo: improve this later on to support XXX" then I would probably implement it immediately after implementing said XXX out of necessity, unless I found a better alternative in the meantime. If it's a "todo: this could be better", that can wait (possibly indefinitely). If it's a "todo: [insert pseudocode]" then it's obviously just a placeholder to give high-level structure to my code until I actually get around to writing it. I try only to submit working and elegant code to the world, with as little todo's, missing bits and warnings (and free of as many bugs as possible) but sometimes nothing else will do in the time given, and you just gotta put that hack in to make the thing work like you want it to..

Edited by Bacterius
0

##### Share on other sites

I wind up doing this with most pieces of code I write that require more than a little bit of thought.  My first pass I code with the sole goal of getting whatever I'm trying to do working. Usually by the time it does work the eloquent, or at least not awful, solution presents itself.

0

##### Share on other sites

it depends, does the "hack" save me several days worth of re-writing code?  Then i can live with it.  however, if i forsee re-doing things to be structured in a better format, and it would save me future work, i'll go back and redesign things if i need to.

void Serialize(Serializer &serializer, std::string &value)
{
//Serialize the string size.
uint16_t size = value.size();
Serialize(serializer, size);
value.resize(size);

//Serialize the characters.
char *cStr = const_cast<char*>(value.data()); //DANGER: Hack. I'm using the .data() function of std::string to write to std::string's internal buffer.
serializer.Serialize(reinterpret_cast<Byte*>(cStr), size);
} 

Potentially very dangerous, but almost certainly workable in major implementations of the standard library - unless the implementation decides to copy its internal buffer at every call to .data(), or unless it has a duplicate buffer for some reason, .data() should return the internal char* buffer of the std::string.

That's clean code but hacky code. I have messier code in several locations, but I don't have anything hackier than that, iirc.

out of curiosity, why didn't you call c_str() instead?

0

##### Share on other sites

c_str is probably more likely to copy the data to put a null terminator on it? He's writing to the string not reading it. That code isn't going to null terminate the string which may violate an assumption the class makes (unless the null terminator is included in the size count). That code looks pretty dodgy to me.

If you read the whole data file into memory beforehand it's probably better to just construct the string from the raw bytes in the file buffer.

I'd just overload Serializer::Serialize to take a string argument though I think. Presumably it knows how to read bytes from the file so I'd read however many is needed then construct the string from the buffer.

0

##### Share on other sites

out of curiosity, why didn't you call c_str() instead?

.data() gets a char* string that is equivalent to the data the std::string holds. (pre-C++11)
.c_str() gets a null-terminated char* that is equivalent to the data the std::string holds.

This means that an implementation is more likely to create a copy of the string data for a call to c_str()... in theory. In practice, they almost certainly return the same string pointer.

In C++11, .data() is also required to be null-terminated. So they are even more almost-certainly going to be the same string pointer. Because of this, I chose the one the more closely documented my intent: I wanted to access the internal buffer (the 'data').

c_str is probably more likely to copy the data to put a null terminator on it? He's writing to the string not reading it. That code isn't going to null terminate the string which may violate an assumption the class makes (unless the null terminator is included in the size count).

.data() in C++11 is guaranteed to be null-terminated, just like c_str(). Since it's already null-terminated when I get the buffer (".........\0"), and I'm not writing over the terminator, the end result is still null-terminated..

That code looks pretty dodgy to me.

Absolutely.   That's what this thread is for, right?
It's pretty much the only true "hack" I have in my code - I have a few messy functions, but no other 'hacks'.

If you read the whole data file into memory beforehand it's probably better to just construct the string from the raw bytes in the file buffer.

I'd just overload Serializer::Serialize to take a string argument though I think. Presumably it knows how to read bytes from the file so I'd read however many is needed then construct the string from the buffer.

Yea, that would be ideal. I wish I could just hand std::string a null-terminated char* buffer for it to take ownership of, and that I could take() ownership of it's own null-terminated char* buffer (with the std::string then becoming an empty string, similar to move-semantics).

The problem with overloading Serializer::Serialize() for a string argument, is the std::string still won't take ownership of the char* buffer you'd read the data into - a completely unnecessary copy of every string read will always take place for no reason, unless you write directly to the string's buffer.

I should actually use &str[0] to get a non-const pointer to the internal data... oops. That'll let me avoid the const_cast, but it'll do the same thing anyway. Really, they ought to just make .data() return a non-const pointer.

Edited by Servant of the Lord
0

##### Share on other sites
I alway refactor/fix my shit unless its in code i havent visited in months (thats legacy code then lol)
0

##### Share on other sites

Always, although I never use TODO for fixes or for hacks (never even used the HACKED task), in fact all I use TODO for is for future additions to the project that I want but don't have time to implement yet. For fixes / hack checking isn't that best done through unit tests and have full code coverage of the area, at least that way it would always fail until you fix it and you can apply the assertion checks globally too if necessary as a fix in one area could just give birth to a bug in another.

Edit: Ofc this isn't always the easiest way to test graphics / sound / scene logic issues but then sticking a TODO flag may not be the most efficient method either, well not for me at least anyway

Edited by Dynamo_Maestro
0

##### Share on other sites

Code can pass any and all unit tests you could write, but still be in dire need of fixing/improvement. Performance issues, working well with future expansion, etc. Lots of reasons to still maintain a ToDo list along side other things.

0

##### Share on other sites

Code can pass any and all unit tests you could write, but still be in dire need of fixing/improvement. Performance issues, working well with future expansion, etc. Lots of reasons to still maintain a ToDo list along side other things.

I agree, I am not saying TODOs are bad I am just saying I never use them for 'fixing' stuff or for anything important for that matter, for extending projects they are great, things typically not part of the project gantt (which is directly connected to my code work items via TFS) but could be a great addition, a wish list I guess. I think my post may have made it look like task lists in general are bad, this isn't the case, im not sure how other version controls work or how others manage but for me it is VERY important for me to ermm analyse, schedule my project work outside code if that makes sense. TFS and ofc VS have some excellent ways to do this, so I cant really use TODO for anything other than a unexpected wish list I guess.

I don't think this is an uncommon way of doing things either, as these are all built in features.

0

##### Share on other sites

That code looks pretty dodgy to me.

I should actually use &str[0] to get a non-const pointer to the internal data... oops. That'll let me avoid the const_cast, but it'll do the same thing anyway. Really, they ought to just make .data() return a non-const pointer.

Hmmm, come to think of it, &str[0] also ensures that compiler implementations doing clever things behind the scenes like copy-on-write strings handle the situation correctly. GCC iirc, still uses COW for relatively recent versions of the compiler. C++11 requires std::string to not be COW, but last I heard GCC hadn't yet changed their std::string implementation. So my const_cast actually could be buggy.

I still think .data() ought to return a non-const pointer.

Edited by Servant of the Lord
0

##### Share on other sites

If you are worried so much about performance you can't read the data into an intermediate buffer, you are probably reading too few bytes of data which is likely inefficient.

0

## Create an account

Register a new account