Am I just a very bad coder or is this an abuse of C++ ?



  • Hi, new guy here. Been lurking around tdwtf for a couple of years but up until now I never needed an account.

    So, long story short, I worked with this guy, generally a great sport btw, very good at math, algorithms and physics, but he really liked pulling stuff like this (changed the class & variable names a little):

    cTextBox* pTc1 = pParentGroup->xCreateChild<cTextBox>();
    pTc1->SetText("This is a test!");
    // more code here ...
    BoundsPropertyModifierSet BPMSet;
    BPMSet.add(new xBoundsPropertyModifierObj<float, &BOUNDS::SetHeight>(pTu1, pParentGroup->GetBounds().GetWidth()));
    // more code here...
    BPMSet.Execute();
    

    Here's what xBoundsPropertyModifier did:

    template< typename T, void ( BOUNDS::*fn1 )(T) >
    class xBoundsPropertyModifierObj: public IPropertyModifierSpec
    {
    public:
    	xBoundsPropertyModifier( cCtrl* pCtrl, const T& tValue )
    		: m_Value(tValue)
    		, m_pCtrl(pCtrl)
    	{
    	}
    	cCtrl* m_pCtrl;
    	T m_Value;
    	/*virtual*/ void Execute()
    	{
    		BOUNDS boundsTemp =  m_pCtrl->GetBounds();
    		(boundsTemp.*fn1)(m_Value);
    		m_pCtrl->SetBounds(boundsTemp);
    	}
    }
    

    Please note that IPropertyModifierSpec above was pure virtual.

    What I want to know:
    Given that the code he left behind is completely architecture like this, is it an abuse of templates ?
    Am I a piss poor coder for barely being able to follow it during my first week with the codebase ?



  • It's not abuse of templates, it is an attempt at transactions. The xBoundsPropertyModifier::Exectue() method ensures, that if the fn1 throws an exception, the bounds are not modified.

    This may be a valid concern, but I don't think it's a valid concern all over the place. Setting height most likely can't throw and even if it did, it probably can't leave the bounds in inconsistent state, because it probably only does one assignment anyway.

    The other thing is that the BoundsPropertyModifierSet is useless. Because if one of the IPropertyModifierSpec::Exectute() methods throws, the Execute()s that already ran still take effect, so you could have just ran them yourself immediately with the same effect. It would make sense if the BoundsPropertyModifierSet itself was creating the temporary and setting it back, but it clearly doesn't do that.

    So it looks like a valid technique that is poorly executed and badly overused. Your colleague probably learnt about exception safety and went out of his way to ensure it.



  • not an abuse of templates

    What's the advantage of passing fn1 in as a template parameter instead of a regular parameter?

    @op, my snap judgment here is that you're not a bad coder. That stuff is spaghetti, and one can only imagine how much bolognaise sauce you've elided within the more code here... bits.



  • In general, I found that code is an abuse of templates if and only if you don't understand what it is for.
    Comments help here.



  • Before I answer your question, I need to point out this is some very poor quality code, because:

    • using explicit types instead of auto/const auto
    • using owning raw pointers
    • exception unsafe
    • assuming T is copyable, instead of using perfect forwarding
    • public fields in a class
    • commented out "virtual" keyword - why?
    • no "override" keyword

    Some only apply to C++11 and above.

    Now, to the point. Technically, this isn't an abuse of templates - it's pretty much an attempt to do a delegate with one parameter. The guy would really get his ass kicked in my code review for reasons stated above (and not using a damn lambda to do this simple job), but technically it's ok. Logically, it's shit, for the record.



  • Worse than that, the code isn't even valid C#!
    You don't have a template keyword in C#, you just write class MyClass<T>.
    And you can't have non-type template parameters, idiot.
    Moreover, what the hell is up with GetBounds () and GetWidth (). There is such a thing as a property, you know? Use it!
    And why would you write a 'public' before the class inheritance? Are you on drugs?
    And I'm not even sure what's going on in the constructor.

    Also, the code doesn't use LINQ, so automatic 0/10 from me.



  • It also isn't valid Java, but I'm actually fine with that.



  • Ohh, soo many languages it is not valid for....I wonder what CreatedToDislikeThis was smoking....

    The "•using explicit types instead of auto/const auto" has many ramifications, and IMPO any hardline (always, never) approach is invariably WTF.


  • SockDev

    @TheCPUWizard said:

    Ohh, soo many languages it is not valid for....I wonder what CreatedToDislikeThis was smoking....

    Or he commenced extraction of a sweet-smelling yellow fluid typically used as a means to dispose of waste amino acids…



  • @TheCPUWizard said:

    The "•using explicit types instead of auto/const auto" has many ramifications, and IMPO any hardline (always, never) approach is invariably WTF.

    Yeah, I'm not a fan of "always/never", but in this case, it applies.



  • @Buddy said:

    What's the advantage of passing fn1 in as a template parameter instead of a regular parameter?

    The value is baked into the code and should be more amenable to inlining. I believe this piece of "wisdom" is obsolete with modern compilers.

    Of course the standard practice is to take functors instead, i.e. anything that can be called, of which function pointers are a special case.

    @NeighborhoodButcher said:

    * using owning raw pointers

    • exception unsafe

    I somehow missed it, but you are definitely right. Which is paradoxical, because the only point of the pattern I can think of is to provide “strong” exception safety.

    Most of the other points just indicate that the code probably isn't C++11, for which there may be a good reason (e.g. compiling for platform for which C++11 compiler was not yet released).



  • @RaceProUK said:

    sweet-smelling yellow fluid

    (emphasis added)
    What on earth are you eating/drinking? Actually, I don't want to know...



  • @Bulb said:

    Most of the other points just indicate that the code probably isn't C++11,

    If the codebase dates from more than 4 or 5 years ago, it's possible that C++11 wasn't even a consideration when the code was written...



  • @CatcherInTheTry said:

    ```
    BoundsPropertyModifierSet BPMSet;
    BPMSet.add(new xBoundsPropertyModifierObj<float, &BOUNDS::SetHeight>(pTu1, pParentGroup->GetBounds().GetWidth()));

    
    Am I miles off-base here, or is that code going to call `BOUNDS::SetHeight()` on the parameter `GetWidth()`?

  • SockDev

    @tar said:

    What on earth are you eating/drinking?

    Stuff with sugar in :stuck_out_tongue:

    Also, never said it was a nice sweetness…



  • I told you not to tell me that! :p



  • @NeighborhoodButcher said:

    Before I answer your question, I need to point out this is some very poor quality code, because:

    using explicit types instead of auto/const auto
    using owning raw pointers
    exception unsafe
    assuming T is copyable, instead of using perfect forwarding
    public fields in a class
    commented out "virtual" keyword - why?
    no "override" keyword

    SomeNearly all only apply to C++11 and above.


    FTFY.

    (And gee, I wonder if the "commented out virtual - why?" has anything to do with the lack of override?)



  • @EvanED said:

    SomeNearly all only apply to C++11 and above.

    FTFY.

    (And gee, I wonder if the "commented out virtual - why?" has anything to do with the lack of override?)

    Yeah, 3 out of 7 is definitely "nearly all". Also, if your method is virtual, add the damned word for everyone else to see it in 0.1s. And it's really confusing to add it and comment it out later., while the method still is virtual.



  • @NeighborhoodButcher said:

    Yeah, 3 out of 7 is definitely "nearly all".

    Here is my reckoning:

    You're just counting the three that are indisputably C++11:
    @NeighborhoodButcher said:

    using explicit types instead of auto/const auto
    assuming T is copyable, instead of using perfect forwarding
    no "override" keyword

    In addition, I put

    commented out "virtual" keyword - why?
    decidedly into the C++11 camp, because I view this is very connected to C++11 -- with no `override` keyword, that is serving as documentation that it's overriding a virtual function in the parent. (It would be a good idea to check before relying on that, but I'd bet up to $50 that's what the programmer was thinking.)

    In addition, I consider

    using owning raw pointers
    to be borderline, because there's not a usable smart pointer in the C++98/03 standard library.

    So by my reckoning, that's 5/7, not 3/7.

    @NeighborhoodButcher said:

    Also, if your method is virtual, add the damned word for everyone else to see it in 0.1s. And it's really confusing to add it and comment it out later., while the method still is virtual.
    The benefit of having it present over not having it present is it indicates it's virtual. The benefit of having it commented out is it indicates that it's virtual because it's inheriting it from the parent class and not a new virtual function in this one.


  • Discourse touched me in a no-no place

    I see a good heated argument building. Mind if we toast some marshmallows on it? We've also got some popcorn somewhere round here, though I think I at least will pass on the beer today (had a decent wine earlier ;))



  • @EvanED said:

    In addition, I put

    commented out "virtual" keyword - why?

    decidedly into the C++11 camp, because I view this is very connected to C++11

    Virtual keyword in overriding methods is not c++11. It's a good way to show that this is overriding something and can be overriden without the need to check inherited classes. The override keyword pretty much does the same these days (in addition to verifying it, of course), but I think it's good to have both for consistency.

    @EvanED said:

    there's not a usable smart pointer in the C++98/03 standard library

    There is - the now deprecated auto_ptr.

    @EvanED said:

    The benefit of having it commented out is it indicates that it's virtual because it's inheriting it from the parent class and not a new virtual function in this one.

    Not really. It doesn't matter if a virtual method overrides something or is added. It's important that it's there and it can be overriden further. You don't care about the previous implementation, as it's not going to be used anyway (unless explicitly called, but that's a detail). Of course such multi level overriding tends to produce unclean code, but that's another problem.


  • SockDev

    @dkf said:

    I see a good heated argument building. Mind if we toast some marshmallows on it?

    It is getting warm in here, now you come to mention it…



  • @NeighborhoodButcher said:

    There is - the now deprecated auto_ptr.
    "usable". auto_ptr is deprecated for a reason.

    @NeighborhoodButcher said:

    It doesn't matter if a virtual method overrides something or is added. It's important that it's there and it can be overriden further. You don't care about the previous implementation, as it's not going to be used anyway (unless explicitly called, but that's a detail).
    I disagree. I think you're looking at it from the perspective of someone looking to make a subclass of, in this case, xBoundsPropertyModifierObj; in that situation, yes, you want to know what behavior you can customize via overriding.

    But If I'm looking to use a class, one question I have to answer is whether to use that class directly or a more generic base class. If I know that xBoundsPropertyModifierObj has all the functions I need but am not sure if the base class does, if I can tell from the definition of xBoundsPropertyModifierObj what functions are provided by the base, that's a useful piece of information. Sure, I could get it by flipping back and forth between the definition of xBoundsPropertyModifierObj and its base class, but having it in one place is nicer.



  • @EvanED said:

    auto_ptr is deprecated for a reason.

    It's deprecated because it doesn't use move semantics, which are quite convenient when dealing with unique ownership. It was usable, but it used the awkward non const not-so-copying constructor. It wasn't deprecated because it didn't do its job. We got a new tool and the old one couldn't be upgraded because of backwards compatibility.

    @EvanED said:

    But If I'm looking to use a class, one question I have to answer is whether to use that class directly or a more generic base class. If I know that xBoundsPropertyModifierObj has all the functions I need but am not sure if the base class does, if I can tell from the definition of xBoundsPropertyModifierObj what functions are provided by the base, that's a useful piece of information. Sure, I could get it by flipping back and forth between the definition of xBoundsPropertyModifierObj and its base class, but having it in one place is nicer.

    If you are looking at leaf classes in order to know if you can use their interface, you are doing it backwards.



  • @NeighborhoodButcher said:

    ...old one couldn't be upgraded because of backwards compatibility.
    No. It was deprecated because it is "moves from lvalues using copy syntax and is thus fundamentally unsafe"1, not because there's now something better. If having a new, better tool was sufficient to deprecate things in C++, there are a lot more things that would meet that bill. It's deprecated because it provides an extremely error-prone API. That's close enough to "unusable" in by book.2

    1 That quote is straight from the horse's mouth BTW.
    2 Technically it could be used, but in many/most cases, it would be equally or less error-prone to use raw pointers.

    @NeighborhoodButcher said:

    If you are looking at leaf classes in order to know if you can use their interface, you are doing it backwards.
    Unless you suspect that you can't use the interface, or have the class's documentation already up, etc.



  • @EvanED said:

    No. It was deprecated because it is "moves from lvalues using copy syntax and is thus fundamentally unsafe."1, not because there's now something better

    That's exactly what I said:

    @NeighborhoodButcher said:

    It's deprecated because it doesn't use move semantics, which are quite convenient when dealing with unique ownership. It was usable, but it used the awkward non const not-so-copying constructor.

    You are literally copying my point here.

    @EvanED said:

    Unless you suspect that you can't use the interface, or have the class's documentation already up, etc

    You always look from top to bottom; from interface to concrete classes. If you want your code to be generic and extensible, your interfaces should be the ONLY thing to look at.



  • I suspect this dispute hinges on the word 'usable',

    Depending upon the implementation of sort, the above line of reasonable looking code may or may not execute as expected, and may even crash!

    If use of auto_ptr<> in 'reasonable looking code' can cause crashes, it's not really 'usable'...



  • I agree there are cases where it causes trouble. I fought with it for years and years until c++11. But in general case it worked. And at some time boost came along with smart pointers, which helped too.



  • @NeighborhoodButcher said:

    You are literally copying my point here.
    You said that it was deprecated because they provided something better. I said they deprecated it because it was bad design. If you want to view those as the same thing go ahead; I don't. (This is not the only distinction I make by any means, but for example: I think there's a decent chance auto_ptr would have been deprecated even if unique_ptr and shared_ptr hadn't been introduced for whatever reason.)

    Looking at the broader conversation: you also said that auto_ptr is usable. By that standard, so are raw pointers. They are about equal in their error-proneness. IMO, any standard that says "code is bad if it uses raw ptrs" should almost certainly say "code is bad if it uses auto_ptrs. So I view your apparent position that "the code is bad because it used raw pointers; it should have at least used auto_ptrs" as being inherently contradictory.

    @NeighborhoodButcher said:

    You always look from top to bottom; from interface to concrete classes. If you want your code to be generic and extensible, your interfaces should be the ONLY thing to look at.
    One reason for that, IMO, is that the information that the commented virtual provides isn't necessarily visible from the subclasses.

    (I will give you the position that if you're on a code base where it is a goal to be able to write everything to interfaces, you shouldn't be looking at the concrete class definitions in the first place; but this isn't most C++ code bases.)



  • @EvanED said:

    You said that it was deprecated because they provided something better.

    Jesus, I actually quoted myself so you could read it without searching. Let me do it again:

    @NeighborhoodButcher said:

    It's deprecated because it doesn't use move semantics, which are quite convenient when dealing with unique ownership. It was usable, but it used the awkward non const not-so-copying constructor

    Why are you arguing when we are saying the exact same thing?

    @EvanED said:

    Looking at the broader conversation: you also said that auto_ptr is usable. By that standard, so are raw pointers. They are about equal in their error-proneness.

    No they're not. Raw pointers give you nothing - no info about ownership or lifespan. You never know if the memory hasn been freed or if you should free it. No exception safety, no automatic management, literally nothing. And the famous dangling pointers. I've been battling C and C++ for most of my life and I can tell you this is a colossal difference. In one case you have something that works for 90% cases, and in other you have something that never works because it can't. Now we don't have that problem (which makes this discussion pointless) and we should be thankful.

    @EvanED said:

    One reason for that, IMO, is that the information that the commented virtual provides isn't necessarily visible from the subclasses.

    I didn't actually understand that.

    @EvanED said:

    I will give you the position that if you're on a code base where it is a goal to be able to write everything to interfaces, you shouldn't be looking at the concrete class definitions in the first place; but this isn't most C++ code bases.

    You always should look at base classes first, if there are any. You're trying to advocate looking at concrete implementations to search for your functionality and than using a commented keyword as a sing the method is in the interface (not to mention, it isn't required to - a comment is just a comment). That is doing things backwards and making own life difficult. No point doing that.



  • Sorry for the delay in replying but I came down with a bit of a cold and felt really sick today.

    First of all, C++ exceptions are not used at all.
    Second, this is 100% C++98 code mostly because:

    1. it uses some old libraries that don't compile under C++11
      and
    2. code needs to potentially compile for targets that have no C++11 support.

    @EvanED
    Virtual keyword is commented because that's how it is in the original source.

    @tar
    Yep, that's all it does. There are even some slightly more complex calls like:

    BPMSet.add(auNew xBoundsPropertyModifier2Obj< float, &BOUNDS::GetTop, &BOUNDS::AllignYUpTo >( pCustomerImage, pTu2, AppScreen::GetScaledValue(10.0f) )); 
    

    Where it calls TWO functions inside that 1 call but that's about it.

    Thanks for reassuring me I'm not THAT s**t of a coder :grin: (not implying that I'm a good coder :smirk:, that's for others to decide during the code review ) ...although if I have to admit I did learn a few new things from this thread.


  • Discourse touched me in a no-no place

    @CatcherInTheTry said:

    First of all, C++ exceptions are not used at all.

    Most C++ authors seem to be extremely keen on avoiding exceptions, probably due to the legacy of truly awful implementations, and some really weird design choices early on that made everything rather difficult to do right. It's therefore quite common for C++ code to be not exception-safe, and rarely a critical problem (well, until one actually happens and the world blows up).

    If you ever switch to writing C# or Java, you'll have to be much more prepared to work with exceptions; those two languages (and their related languages that use the same underlying runtimes) have solid exception systems and use them extensively.


  • SockDev

    @dkf said:

    If you ever switch to writing C# or Java, you'll have to be much more prepared to work with exceptions; those two languages (and their related languages that use the same underlying runtimes) have solid exception systems and use them extensively.

    In the case of Java, you're forced into handling them; at least C# makes it optional :smile:



  • Oh, I've had my share of java alright. With J2EE & J2ME in the past and with android nowadays.

    On a different note, the legacy codebase (that we got from the previous team) had this little gem in it (obviously edited, just like last time...).
    Again, this code is not necessarily wrong per se it's just ... weird.
    Yes, I guess it's faster than parsing but...

    struct DATA
    {
    	char		cFormat[ 4 ];
    	short		sMajorVersion;
    	short		sMinorVersion;
    	DATA_CHUNK* 	pDataChunks;
    	int		iDataChunksNum;
    // ... more stuff here
    };
    
    struct DATA_CHUNK
    {
    	CUSTOMER_DATA*	pCustomers;
    	int		iCustomersNum;
    
    //	...More arrays defined just like the one above
    };
    
    // And loaded like this
    DATA* pDATA = (DATA*)pSrc;
    
    int iPos = sizeof( DATA );
    
    iPos = align( iPos, DATA_CHUNK_ALIGN );
    
    if( pData->iDataChunksNum)
    	pData->pDataChunks = (DATA_CHUNK*)(pSrc + uiPos);
    else
    	pData->pDataChunks = null;
    
    iPos += sizeof( DATA_CHUNK ) * pData->pDataChunksNum;
    
    DATA_CHUNK* pDataChunk = pDATA->pDataChunks;
    if( pDataChunk != NULL )
    {
    	for( i=0; i!=m_pData->iDataChunksNum; ++i,++pChunk )
    	{
    		uiPos = align( iPos, DATA_CHUNK_ALIGN );
    		if( pDataChunk->iCustomersNum )
    			pDataChunk->pCustomers = (CUSTOMER_DATA*)(pSrc + iPos);
    		else
    			pDataChunk->pCustomers = NULL;
    
    		        iPos += sizeof( DATA_CHUNK* ) * pDataChunk->iCustomersNum;
    
    		        CUSTOMER_DATA* pCustomer = pDataChunk->pCustomers;
    		        for( j=0; j!pDataChunk->pCustomers; ++j,++pCustomer )
    		        {
    				uiPos = align( uiPos, AGS_FRAME_ALIGN );
    				if( pCustomer->sSomeOtherDataNum )
                        		pCustomer->pSomeOtherData = (SOME_OTHER_DATA*)(pSrc + uiPos);
                    		else
    					pCustomer->pSomeOtherData = NULL;
    
    				// It goes on like this for another 3 indentation levels
    			}
    		}
    }
    

    Try to guess what happened when we compiled for 64 bit processors...



  • @dkf said:

    It's therefore quite common for C++ code to be not exception-safe

    As Raymond Chen pointed out, a lot of the time it's hard to verify if C++ code is exception-safe or not, whereas error-checking is blatantly obvious.

    My guess is that the C++ use of RAII instead of finally blocks like a "normal" language is probably a contributing factor.


  • Discourse touched me in a no-no place

    @RaceProUK said:

    at least C# makes it optional

    Up until the point where you want to figure out what exceptions are going to blow out of a piece of code so you can check you're handling them right. Java's long-winded, fussy and very bureaucratic, but that can sometimes help. (Most of my code does very little with exceptions except for letting them bubble outwards; I have handlers applied with AOP which do neat things when they happen, such as converting them to appropriate error pages.)


  • Discourse touched me in a no-no place

    @CatcherInTheTry said:

    Try to guess what happened when we compiled for 64 bit processors...

    Ugh! Ugh, ugh, ugh, ugh, ugh. (I take it you're compiling this as C?)

    It's nearly 20 years since I wrote something as nasty as that, and in that case it was because we were desperate to try to fit as much as we could into memory (2GB of RAM was expensive back then) and so resorted to sub-word bit stuffing and some elaborate compression techniques. To see such things in modern code? I refer you to my previous paragraph. Ugh.


  • BINNED

    @RaceProUK said:

    In the case of Java, you're forced into handling them;

    Obligatory: In php as well. Sometimes. When it feels like it. I mean, it is a Fatal error but it will just chug along anyway.


    Filed under: Commence tail /var/log/apache2/error.log since the original dev was lazy and suppressed all error messages, Oh, shared hosting? Good luck then!



  • Yes, why how on earth did you guess :smirk: ?

    Wow... 20 years... man I'm young.

    That was written somewhere in between 2002-2006... i think...
    Also, just to clarify the code has nothing to do with customers and suppliers, I just replaced it with that.
    Basically we have an old tool (from 2006, binary only obviously :sob: ) that basically takes in... let's say excel files and parses them into that binary format that we then read into the app.


  • Discourse touched me in a no-no place

    @CatcherInTheTry said:

    Yes, why how on earth did you guess ?

    I've been writing C for a long time. That code has a certain… smell about it.

    @CatcherInTheTry said:

    Wow... 20 years... man I'm young.

    You get there one day at a time. It's a good idea to get in the habit of writing your code to be as clear as possible; it only comes back to haunt you if you don't. :smiley:

    @CatcherInTheTry said:

    That was written somewhere in between 2002-2006... i think...

    That puts it about 10 years younger than my stuff. By that point, usefully 64-bit machines were a much more common thing (if rather pricy) so crazy bit stuffing was a much worse idea than before.

    When you refactor this thing to make it non-nauseating, write one function to handle each structure (and which can recursively call the other functions as required). Like that you don't have to get the full horror in your head at once.



  • The biggest lesson that 64bit taught me was that sizeof(size_t) != sizeof(int32_t)...


  • Winner of the 2016 Presidential Election

    "Fun" C++ fact: ::std::size_t and the size type of standard containers are not guaranteed to be the same type.



  • @asdf said:

    "Fun" C++ fact: ::std::size_t and the size type of standard containers are not guaranteed to be the same type.

    That's not "fun"! That's actually very annoying! :(

    Is there even any guarantee that, say, std::vector<T>::size_type is the same type as std::list<T>::size_type?


  • Winner of the 2016 Presidential Election

    IIRC, no, there isn't.


  • Discourse touched me in a no-no place

    @tar said:

    Is there even any guarantee that, say, std::vector<T>::size_type is the same type as std::list<T>::size_type?

    Why didn't they just make it a defaulted template parameter? Like that you'd be able to make it be exactly what you wanted (at the cost of making the readability of compilation errors even worse).



  • @dkf said:

    When you refactor this thing to make it non-nauseating,

    Oh, you must mean Never because, as in all corporate environments, that's all the time we'll get for refactoring anything. :smile:
    I was the one in charge of that task, and I wrote an even dirtier hack around that (so I guess I CAN be a shit coder sometimes, as in a coder that writes SISO -> st goes in, st comes out).
    Had to do it in 5 days, 2 spent "researching :smirk: ", and 3 days to implement (and test for most of the the horribly mangled edge cases).

    Then again it could be worse... :grimacing: At least I have a job... :open_mouth:



  • @dkf said:

    Why didn't they just make it a defaulted template parameter? Like that you'd be able to make it be exactly what you wanted (at the cost of making the readability of compilation errors even worse).

    Is it perhaps determined by the allocator defaulted template parameter?
    std::allocator<T>::size_type and the like?

    It seems like it'd be a sensible thing to do, but I'm not going to sludge through the C++ gospels to find out.



  • Probably because that would have been a sensible thing to do...

    Now I'm wondering whether there are any situations where something like size_t c = vector.size(); is actually going to bite me on the as...



  • @CatcherInTheTry said:

    Oh, you must mean Never because, as in all corporate environments, that's all the time we'll get for refactoring anything.

    "What do you mean, you want to spend a week working on this code and we won't see any new features at the end of that time? How could that possibly be of any benefit to the company? Are we not giving you enough new features to implement? Because we have a lot of ideas for new features..."



  • So true, and we've recently decided to implement "agile" as in that kind of agile, you know the one where we still have to do all those features in that exact same order with the same deadline, only now we have to figure out ways of not getting in each other's way, fill worksheets in a webapp, and waste about 3/4 of a workday every week.
    At least the agile trainer chastises our boss and he seems to be willing to listen... for now...

    Okay, I'm going to stop ranting now. And probably discussing this subject. If I do come across other particularly bad or at least very weird pieces of code I may post them here... you know... as a learning experience.


Log in to reply
 

Looks like your connection to What the Daily WTF? was lost, please wait while we try to reconnect.