back to article What Compsci textbooks don't tell you: Real world code sucks

There’s a kind of cognitive dissonance in most people who’ve moved from the academic study of computer science to a job as a real-world software developer. The conflict lies in the fact that, whereas nearly every sample program in every textbook is a perfect and well-thought-out specimen, virtually no software out in the wild is …

COMMENTS

This topic is closed for new posts.

Page:

      1. Mage Silver badge

        Re: "nearly every sample program in every textbook is a perfect"

        Macros are evil. Period. One thing I've agreed with Stroustrupp since 1987 on first reading his stuff. Actually most of the bad stuff in C++ may be due to AT&T insisting on as much C compatibility as possible. The first C++ compilers I used actually only pre-processed C++ text to C text creating the magic pointer "this" as a first extra parameter in the C text.

        1. Anonymous Coward
          Stop

          Patently UNTRUE: "Macros are evil. Period"

          I assume you never used a proper macro language such as m4. I assume you never used the -E option of gcc.

          Here is an example of an m4-based vector (and a hashtable) class and I challenge you build something equivalent with templates. Something which gives sane error messages, which will allow for proper code navigation (where is the payload constructor called ??), which allows for proper debugging of the data structure. I bet you can't get it done.

          So your statement holds some water if you refer to "C macros", but even then, some people manage to build huge systems (2000 modules and bigger) based on macro-constructed container classes. E.g. CATIA.

          //vector class

          //$1: type of payload

          define(generate_vector,

          class vector_$1

          {

          $1* _array;

          unsigned long long _length;

          unsigned long long _capacity;

          public:

          vector_$1()

          {

          _array = new $1[1];

          _length = 0;

          _capacity = 1;

          }

          void operator = (const vector_$1& other)

          {

          ensureCapacity(other._length);

          _length = other._length;

          for(unsigned long long i=0; i < _length; i++)

          {

          _array[i] = other._array[i];

          }

          }

          vector_$1(const vector_$1& other)

          {

          _array = NULL;

          _length = 0;

          _capacity = 0;

          *this = other;

          }

          ~vector_$1()

          {

          _length = 0;

          _capacity = 0;

          delete[] _array;

          _array = NULL;

          }

          unsigned long long length() const

          {

          return _length;

          }

          void add($1 element)

          {

          ensureCapacity(_length + 1);

          _array[_length++] = element;

          }

          void ensureCapacity(unsigned long long count)

          {

          if(count >= _capacity)

          {

          unsigned long long newCap = _capacity * 2;

          if(newCap < count)

          {

          newCap = count;

          }

          $1* newArray = new $1[newCap];

          for(unsigned long long i=0; i < _length; i++)

          {

          newArray[i] = _array[i];

          }

          _capacity = newCap;

          delete[] _array;

          _array = newArray;

          }

          }

          void getAt(unsigned long long index,$1& retVal) const

          {

          if( index >= _length)

          {

          //deterministically crash upon out-of-bounds.

          //core file can be easily debugged

          int* crashPtr = NULL; crashPtr[0] = 0;

          }

          retVal = _array[index];

          }

          void setAt(unsigned long long index,const $1& val)

          {

          if( index >= _length)

          {

          //deterministically crash upon out-of-bounds.

          //core file can be easily debugged

          int* crashPtr = NULL; crashPtr[0] = 0;

          }

          _array[index] = val;

          }

          $1 operator[](unsigned long long index)

          {

          if( index >= _length)

          {

          //deterministically crash upon out-of-bounds.

          //core file can be easily debugged

          int* crashPtr = NULL; crashPtr[0] = 0;

          }

          return _array[index];

          }

          const $1 operator[](unsigned long long index) const

          {

          if( index >= _length)

          {

          //deterministically crash upon out-of-bounds.

          //core file can be easily debugged

          int* crashPtr = NULL; crashPtr[0] = 0;

          }

          return _array[index];

          }

          };

          )

          //$1: type of key

          //$2: type of value

          //$3: type of HashHelper

          define(generate_hashtable,

          struct entry_$1_$2_$3

          {

          $1 _key;

          $2 _value;

          entry_$1_$2_$3* _nextInBin;

          bool _isOccupied;

          entry_$1_$2_$3()

          {

          _nextInBin = NULL;

          _isOccupied = false;

          }

          };

          class hashtable_$1_$2_$3

          {

          entry_$1_$2_$3* _bins;

          unsigned long long _numBins;

          unsigned long long _numElements;

          static void insert(entry_$1_$2_$3* bins,

          unsigned long long binCount,

          const $1& key,

          const $2& value)

          {

          unsigned long long index = $3::hash( key ) % binCount;

          if( bins[index]._isOccupied )

          {

          entry_$1_$2_$3* ptr = &bins[index];

          entry_$1_$2_$3* lastPtr;

          do{

          lastPtr = ptr;

          if( $3::equals(ptr->_key,key) )

          {

          break;

          }

          ptr = ptr->_nextInBin;

          }

          while( ptr != NULL );

          entry_$1_$2_$3* newEntry = new entry_$1_$2_$3;

          newEntry->_isOccupied = true;

          newEntry->_key = key;

          newEntry->_value = value;

          lastPtr->_nextInBin = newEntry;

          }

          else

          {

          bins[index]._isOccupied = true;

          bins[index]._key = key;

          bins[index]._value = value;

          }

          }

          public:

          hashtable_$1_$2_$3()

          {

          _bins = new entry_$1_$2_$3[1];

          _numBins = 1;

          _numElements = 0;

          }

          ~hashtable_$1_$2_$3()

          {

          for(unsigned long long i=0; i < _numBins; i++)

          {

          if( _bins[i]._isOccupied )

          {

          entry_$1_$2_$3* ptr = &_bins[i];

          do{

          entry_$1_$2_$3* tobeDeleted = ptr;

          ptr = ptr->_nextInBin;

          if( tobeDeleted != &_bins[i] )

          {

          delete tobeDeleted;

          }

          }

          while( ptr != NULL );

          }

          }

          delete[] _bins;

          _numBins = 0;

          _numElements = 0;

          }

          void insert(const $1& key,const $2& value)

          {

          if( _numBins <= _numElements )

          {

          //grow table

          unsigned long long newSize = _numBins * 2;

          entry_$1_$2_$3* _newBins = new entry_$1_$2_$3[newSize];

          for(unsigned long long i=0; i < _numBins; i++)

          {

          if( _bins[i]._isOccupied )

          {

          entry_$1_$2_$3* ptr = &_bins[i];

          do

          {

          insert( _newBins, newSize, ptr->_key,ptr->_value );

          entry_$1_$2_$3* tobeDeleted = ptr;

          ptr = ptr->_nextInBin;

          if( tobeDeleted != &_bins[i] )

          {

          delete tobeDeleted;

          }

          }

          while( ptr != NULL );

          }

          }

          delete[] _bins;

          _bins = _newBins;

          _numBins = newSize;

          }

          insert( _bins, _numBins, key, value );

          _numElements++;

          }

          bool lookup(const $1& key,$2& value)

          {

          unsigned long long index = $3::hash( key ) % _numBins;

          if( _bins[index]._isOccupied )

          {

          entry_$1_$2_$3* ptr = &_bins[index];

          do

          {

          if( $3::equals(ptr->_key,key) )

          {

          value = ptr->_value;

          return true;

          }

          ptr = ptr->_nextInBin;

          }

          while( ptr != NULL );

          }

          return false;

          }

          };

          )

          #include <stdlib.h>

          namespace MSL

          {

          generate_vector(int)

          generate_vector(PayloadComplex)

          generate_hashtable(string,PayloadComplex,PayloadComplexHasher)

          generate_hashtable(int,int,intHasher)

          }

          1. Anonymous Coward
            Stop

            Re: Patently UNTRUE: "Macros are evil. Period"

            Some more code:

            //must be defined before macro-generated header

            class PayloadComplex

            {

            char* _name;

            char* _street;

            char* _city;

            unsigned int _postcode;

            public:

            PayloadComplex(const char* name,

            const char* street,

            const char* city,

            unsigned int postcode)

            {

            _name = strdup(name);

            _street = strdup(street);

            _city = strdup(city);

            _postcode = postcode;

            }

            PayloadComplex()

            {

            _name = NULL;

            _street = NULL;

            _city = NULL;

            _postcode = 0;

            }

            PayloadComplex(const PayloadComplex& other)

            {

            _name = NULL;

            _street = NULL;

            _city = NULL;

            _postcode = 0;

            *this = other;

            }

            void operator = ( const PayloadComplex& other)

            {

            free(_name);

            _name = strdup(other._name);

            free(_street);

            _street = strdup(other._street);

            free(_city);

            _city = strdup(other._city);

            _postcode = other._postcode;

            }

            void print()

            {

            cout << "PayloadComplex: name: " << _name <<

            " street:" << _street <<

            " city:" << _city;

            }

            ~PayloadComplex()

            {

            free(_name); _name = NULL;

            free(_street); _street = NULL;

            free(_city); _city = NULL;

            _postcode = 0;

            }

            };

            class PayloadComplexHasher

            {

            public:

            static unsigned long long hash(const string& key)

            {

            unsigned long long accumulator = 31415;

            int l = key.length();

            for(int i=0; i < l; i++)

            {

            accumulator += key[i];

            accumulator = (accumulator * 17) ^ 767612781 ^ (accumulator * 9318);

            accumulator ^= ( (accumulator >> 8) ^

            (accumulator >> 7) ^

            (accumulator >> 6) ^

            (accumulator >> 32)

            );

            }

            return accumulator;

            }

            static bool equals(const string& keyA, const string& keyB)

            {

            return ( keyA.compare(keyB) == 0 );

            }

            };

            class intHasher

            {

            public:

            static unsigned long long hash(int key)

            {

            unsigned long accumulator = 31415;

            for(int i=0; i < 3; i++)

            {

            accumulator ^= key;

            accumulator = accumulator * 17 + 77612;

            accumulator = accumulator ^ (accumulator >> 16) ^ (accumulator >> 23) ^ (accumulator >> 7);

            }

            return accumulator;

            //return key;

            }

            static bool equals(int keyA, int keyB)

            {

            return keyA == keyB;

            }

            };

  1. NomNomNom

    they never told me there were textbooks

  2. Bernard

    This issue isn't unusual to computer science

    The legal system and our informal cultural norms are exactly the same.

    The theoretical elegance of beginning anything from a blank page is trumped by the practical utility of working with what's already there and tinkering where necessary.

    Thats why we're still nominally subjects of a monarch, why non-christians frequently celebrate christmas and why company policies and procedures rarely bear any relation to the actual workings of the firm.

    1. Brewster's Angle Grinder Silver badge

      Re: This issue isn't unusual to computer science

      Although parliament generally patches amends existing legislation, it does sometimes rewrite a law from the ground up. We also have the Law Commission who's job is to refactor tidy up existing laws.

      There is no reason refactoring days or permanent refactoring parties couldn't be a stand part of the dev tool kit. Just as there's no reason code review couldn't be a standard part of the dev tool kit. These issues are cultural.

    2. Primus Secundus Tertius

      Re: This issue isn't unusual to computer science

      It is also why Christians celebrate the Roman Saturnalia or the Teutonic Yuletide.

  3. VaalDonkie

    TIim is money

    When a client suddenly decides 75% of the way through the project that something integral to the system should work completely differently, few project managers would have the crown jewels necessary to start a new project from scratch. Instead, one will have to find the quickest way possible to deliver the project on time and within the original budget.

  4. Eclectic Man Silver badge
    Unhappy

    I've seen something like this before ...

    I can't help feeling that the specifc example ofpoor code, is very similar to poor management. As a company starts trying to 'do' things, people find their own ways of doing things that are not clearly written down in agreed policies and procedures (many of which do not quite work or leave gaps).

    This eventually results in a Byzantine system of quirky 'fixes' which some people do, and wich are inconsistent with other people's views. As one manager said to me once "If I find that I want to do something and the rules do not allow it, I ... break the rules." My response "I prefer rules that work", surprisingly did not go down particularly well. (I find that where people have to break the rules to get the work done, each person breaks the rules in a different way.) The theory learnt in an MBA seems all well and good, until you start experiencing the problems caused by your own decisions - much better to get a job with one of the 'big four' consultancies and watch your 'vauled clients' sort out the problems you gave them with your'theoretically correct but functionally incomplete and unsound 'advice'.

    As for code writing, if I draw the flowchart first and get that to work, the only errors in my code are typographical, if I don't draw the flowchart first, I spend about 5 times longer fixing algorithmic (and typographical) problems.

    Merry Christmastide everyone.

    1. Anonymous Coward
      Anonymous Coward

      @Eclectic Man

      I tried to convince management to make a plan with me so that the business and IT can agree on what needed to be done and have a reasonable idea of how long. However management would make the plan, then change it daily until long after the project deadline and something more important came up.

      I insisted on receiving changes by email so I had a chronological log of decisions, especially when they conflicted. The biggest mistake made by a newly appointed IT manager was to implement a bug track system for requesting changes and then giving management access to it. Days later he admitted he should have followed my advice and restricted access to just IT. Needless to say the projects continued to drag even further past the deadlines and original designs had no resemblance to the huge list of conflicting requirements.

    2. Someone Else Silver badge
      Meh

      Flowcharts?

      How quaint.

      1. Primus Secundus Tertius

        Re: Flowcharts?

        Not quaint. It is extremely good to be able to see the logic in two dimensions. Examples: that both outcomes of an if-test are properly handled; that loops are properly constructed; case statements etc.

        If the loops are constructed to follow best practice, they can be described in equivalent one-dimensional language; but they cannot be visualised.

  5. silent_count

    The cycle which produces crap code

    So long as the upper management have their shiny iPads and colourful Power Points to play with they don't want to get "bogged down" in the 'requires more than a 20 second attention span'... I mean, "trivial" details of how their computer systems work. They're busy people and have important work to... ooh look! The picture rotates if I turn my iPad around.

    So the middle managers get paid for delivering projected on budget. If it costs $1k to write and $1 million per year to maintain, the manager has already gotten their bonus and gone on their merry way. If it costs a $1 million to write and $1k per year to maintain, they're out of a job because a "better" middle-manager could have delivered the 'exact same' project for a mere $1k.

    Sure, the numbers are exaggerated but the phenomenon isn't. Oh yeah, and the middle-managers who deliver the cheapest results percolate towards the top and become the upper-management, and the cycle continues.

  6. Some Beggar
    Meh

    "almost all of them in the banking industry"

    Now I don't want to veer into any general derision towards the process and quality control of the financial industry or the competence of engineers who work in backroom banking ... but I think you may have answered your own question here.

    I've worked with embedded software in medical devices and network/consumer comms products for *cough* years and with a couple of notable (and horrifying) examples - typically code that was older than methusela and that nobody dared breathe on let alone re-factor - the quality of the code has been pretty good.

  7. Anonymous Coward
    Anonymous Coward

    Money, actually...

    It all boils down to money. "Management" won't pay enough to get adequately skilled staff, and available time is always constrained by budgets.

    1. Mage Silver badge

      Re: Money, actually...

      I've been working with programmers since 1978. I'm not convinced very many skilled programming staff exist.

      1. Primus Secundus Tertius

        Re: Money, actually...

        I agree. I used to tell outsiders that programming is logic on large scale, and few people in the industry are very good at it.

        Then you look at all the duff medics, accountants, managers, etc and you shrug it all off.

      2. ThePhaedrus
        FAIL

        Re: Money, actually...

        >> I've been working with programmers since 1978. I'm not convinced very many skilled programming staff exist.

        It's always the others that are lacking the skills, isn't it?

      3. Mark Pawelek

        Re: I'm not convinced very many skilled programming staff exist.

        It's still a money problem. If skilled programmers aren't around they could be trained ... but ... "that will cost too much"

  8. Peter Fox

    I, I, I, me, me, me

    Of course programmers have individual styles. Of course yours displeases me for any number of reasons. Now we've dealt with your terrible style we can move on to your structural, conceptual and logical ignorance, mis-application and inadequacies.

    Hey! Why not work together so I can rein-in your weirdnesses and you can admire and learn from my vision and perfection of implementation. Every day! If you're capable I'll let you do some of the easier bits for me and in return you can test my code and fix trivial bugs for me. Aren't I good to you.

    The serious point is that we have to work alone in some respects but should be working together more. I'm not advocating two people with one needle each trying to knit, but if you're writing code for somebody else then it's not your precious secret.

    1. Anonymous Coward
      Anonymous Coward

      Re: I, I, I, me, me, me

      Peter, this is called 'pair programming'. It's very useful, and surprisingly, newer AGILE methods highly recommend this methodology of development. :-)

      I know, I'm preaching to the converted... ;-)

  9. ForthIsNotDead
    Meh

    Just

    Keep your teams small. Programmers perform much better when they have the big picture, and feel that they are a proper member of the development team. A large team will lead to large, overly-complex, bloated code, for no other reason than Johhny Smith, who has been given the task of "unifying the button handler and dispatcher code" will turn it into a lifes work, simply because he has nothing else to do and can make no other contribution.

    Keep your teams small, so that each member is covering quite a large amount of the "solution surface area" (yacky phrase).

    Time required to complete a project is inversely proportional to the number of people involved in it. Not just coders. Project managers, planners, workpackage engineers etc.

    Read "The Mythical Man Month" by Fred Brooks, an ex IBM Project Manager who saw first hand how to guarantee a total fubar project...

    http://en.wikipedia.org/wiki/The_Mythical_Man-Month

    1. Primus Secundus Tertius

      Re: Just

      One of the main points Brooks made was that the best programmers are much much better than the average.

      So then you can succeed with a small project team.

  10. Anonymous Coward
    Anonymous Coward

    Don't use C++ or Perl if there is more than one programmer

    People who advocate those languages always say: "Yes, but you don't have to use all those weird/dangerous features if you don't want to." However, if you have a project with more than one programmer, in the absence of an effective mechanism to prevent people from using weird/dangerous features of the language, someone will use them, and then everyone will have to cope with the consequences.

    Haskell turns out to be surprisingly good for large projects with multiple programmers. It's a lazy, functional language, so it takes a while to learn for someone who's not used to that style of programming, but once you've understood the principles there's really not much to learn or remember, and the module system is excellent.

    The main problem with Haskell that I've encountered is that the performance can be hard to analyse: most of the time it runs fast enough, but occasionally it doesn't and then it can be difficult to understand why and what to do to improve it.

    1. Anonymous Coward
      Anonymous Coward

      Re: Don't use C++ or Perl if there is more than one programmer

      I find the whole functional programming paradigm to be beautiful. Haskell is certainly excellent for large projects with multiple programmers, once they've had the shackles of imperative programming removed.

      I agree that there are some aspects of performance that are hard to analyse, due to laziness, and a few of the big name companies that say they use Haskell actually use a strict in-house variant. However, most of the time the problems boil down to finding a balance between your programs being too lazy, which can build up large unevaluated thunks in memory, and your programs being too strict, which removes all the advantages of a lazy evaluation strategy.

    2. Anonymous Coward
      Thumb Down

      Not True

      If you have skilled developers, proper architecture, proper coding practices (such as not using implicit Perl variables too much), proper documentation, then you can easily have several developers working with C++ or Perl on a single project. Of course, you also need proper management.

      Don't blame a lack of human skills on the language. And, don't tell me you can build large-scale, complex systems such as firefox, openoffice or postgresql in $functionalLanguage. You can build something, but it will perform poorly. It will freeze every minute (on average) for GC.

  11. Terry 6 Silver badge

    Using software

    I'm just a user. (OK a skilled user).

    Hell I know there's crap programming out there. It's hard not to notice when the installation of newly released driver for a piece of equipment from a major company falls over, often because there's a file that didn't get removed, and can't be overwritten. But the old driver has now been killed, and Hey that won't reinstall either- for the same reason.

    Or the firmware upgrade for that shiny superhub that connects all your devices to the internet means that it routinely locks out those devices until it's rebooted ( or randomly reboots itself).

  12. Electric Panda
    Flame

    Can relate to this

    This is all so true. An alarming proportion of "production" code I've seen is awful; poorly documented, shoddily written spaghetti which you are usually expected on pain of death to understand intricately within a nanosecond of opening the project.

    Design patterns? Pfft. Data structures? No need, just use java.lang.io.SuperDuperSwissArmyKnifeSort or the home rolled com.enterprise.selfaware.collectionsframework.overengineerednonsense.doesthesameasthejavaoriginal.broken class instead. The latter was last updated in 2004 by someone who since left the company.

    If I handed in that sort of code as an academic assignment, I could literally have failed. Whoever wrote that code got paid and the horrid code ended up in production.

    What a lot of students also don't realise is that there's often very little new coding going on. Most junior/entry level roles involve taking some self-aware Cthulhu written in an outdated version of Java and poking it with a stick - horrid grunt work the regular staff really deliberately and actively avoid.

    Like all other CS graduates I initially felt that going from university into a coding role was a natural progression. After just one internship I realised the absurdity of it (and reminded myself of how much I hate programming from scratch, although I write scripts to automate things and wrap around pre-existing solutions as required), went back to university to do an InfoSec MSc and now work as a penetration tester and network security analyst. Much more satisfying, much brighter prospects and a good bit better paid.

    What's really funny is that universities just focus non-stop on programming these days, yet the graduates are awesomly ill-prepared for what real programming and software development actually is.

  13. fch

    "textbook perfect code" ...

    ... is rarely found in textbooks, from my experience.

    Why is that so ? Because textbook code is, by virtue of its objective - teaching coding - too simple and too small for the issues associated with paid-for / commercial software development to pop up. It avoids covering issues encountered in developing software not strictly associated with the technical act of coding itself.

    Amongst those, in no particular order:

    Teamwork - the absolute necessity to either work with other programmers or work with project managers, business requirements, your boss and her/his budget - is usually absent from textbooks. If no framework for this exists in the software project at all, it'll end up with interesting "warts" - think of massive code diffs in series of subsequent commits for no other reason than two developers having set their IDE's to "auto-indent" with different rules. Or imagine the odd little key component in an otherwise-pure-Java project pulling in Jython plus a bunch of 50 python modules including a few binary components written in C++, because one developer got away with writing a 50-line piece in Python using those modules since that was so much simpler and faster than the 500 lines it'd have taken in Java. Think about the "write to spec" warts that occur because specs and tests are done first for "validate known-to-be-valid input only", the code is written that way, and half a year someone using it is terribly surprised about the security holes blown into it by obviously-invalid input ... never spec'ed, never tested, never requested ... and no two-way communication anywhere between requirements mgmt, technical architects, project managers, testers and coders. And where in a textbook have you ever seen an example covered so big that you _couldn't_ implement it on your own in a month (Knuth's level 3.5+ exercises notwithstanding) ?

    Complexity - textbooks often are extremely brief on this one, and C.Hoare's "The Emperor's old clothes" is quoted nowhere near as often as it should be. Abstraction, Encapsulation, Specialization, Generalization and other "layering" patterns are often covered extensively from the "how to do it" perspective but warnings about the dangers of extensive layering, respectively guidelines on "good usage" (do you really need to subclass if all you want is to have an object instance that differs from the default-constructed one in a single property ?), or, to put it differently, "when not to use it", are often absent. The coverage of interfacing with pre-existing libraries is often scant. As a consequence, have bunch of new interface layers here, a new generalization there counteracted by an additional specialization elsewhere, and have multiple sequences of conversions resulting a chinese whisper chain of data transfers. Every layer on its own might be a great, well-written, perfectly documented and admired piece of code ... but the combination screams at you just as much as the result on the floor of a night drinking great wine after a michelin-starred ten course meal.

    Libraries - face it, you're not going to write everything from scratch. But which Java programmer knows even a third of the Java 7 classes by name ? Which C++ programmer can use a third of Boost without the manual ? Which C/UNIX programmer knows how to use a third of Linux syscalls ? And that's only _standard_ libraries. Not to talk about the vast worlds of things like Perl or Python's component archives. Effective programming often means effective research into what existing library already-in-use-elsewhere in the project will provide the functionality that you've been considering reimplementing from scratch. Which book teaches this ?

    Legacy - textbooks like to advertise the latest-and-greatest; of course it's a necessary of work life to keep your skills up, and to follow changes and enhancements in the technologies touching your area of expertise, but even more so is it important to develop "code archaeology skills". That is, go backward in time and check out what was considered good practice in 1990, what was cutting edge in 1995, which code patterns and styles were considered best in 2000. Go to the library and read a book on J2EE from 2002 as well as the newest one on Java 7, then grab a 1992 and 2012 Stroupstrup. Read and compare the Documentation/ subdirectory of the Linux kernel sources in the 2.2 and 3.4 versions. Much of this reading will trigger "duh" moments of the same sort that you'll inevitably encounter when you start to look at existing code. Nonetheless, textbooks (or, even more so, lessons/lectures) that emphasize how mistakes are identified, rectified and avoided in future projects are barely existant, else F.Brooks "mythical man month" wouldn't still be so popular more than 40 years after it was written.

    Evolution - coding, and I object to adherents of "software design" here - is never intelligently designed. In the beginning, it's created, and from that point on it evolves. It grows warts, protrusions, cancers that are hindering its adaptibility to the tasks it's set to just as much as new nimble limbs and telepathic powers to help it. A wart for one usecase can be the essential sixth finger for another. Code Veterinary can be a dirty job, and the experience of humility involved with dragging the newborn out past the cr*p of the parent is not something you're prepared for by reading about it, because books don't smell. Often enough there'll be the revolting ethical challenge of becoming a Code Frankenstein as well - when you'll stich a few dead protrusions dug out of a code graveyard by someone onto the already-mistreated beast. It's like textbooks that tell you about how to have fun making babies but not about how to change their nappies or how to deal with teenagers having a fit.

    All of these one can learn to live with; none of these are necessarily perpetuated ad saeculum saeculorum.

    Thing is, "textbook perfect" isn't the same as "beautiful" and that isn't the same as "best for the usecase". The grimy bit about software development is that a really beautiful solution can be built from frankensteinish code, and to develop the skills as well as the thick skin to be able to do this, no textbook I've seen will prepare you for.

    (I still like coding, but sometimes I think I should'be become a vet)

  14. graeme leggett Silver badge

    Obviousness of bad code - some Friday thoughts

    From what I've read here today, coders know what bad code looks like and that its something they have to write themselves due to the limitations of the situation. Presumably managers either don't know, don't care, or are also mindful of the limitations of "get it done now" and "no, there is no more overtime".

    To a non-coder all code looks like a mess of numbers and letters, and bad code looks no worse than good code. Is there a parallel with mechanial engineering where it is said "it it looks right, it is right" ?

    Perhaps I've got a "practical" mind, if you took the back off a piece of equipment I could (after some peering about) guess whether it was a bit of a lash up or a properly laid out mechanical masterpiece - a mix up of cables and gears or nicely routed and pinned wiring.

    Are there tools that can represent code into a schematic form to make it easier to see how much of a lash-up they are?

    1. John Smith 19 Gold badge
      Unhappy

      Re: Obviousness of bad code - some Friday thoughts

      "Are there tools that can represent code into a schematic form to make it easier to see how much of a lash-up they are?"

      Yes.

      Will management buy them. No.

      Will developers use them. Maybe.

    2. I ain't Spartacus Gold badge

      Re: Obviousness of bad code - some Friday thoughts

      graeme leggett,

      Very true. I sometimes have to get my hands dirty and get building plans, to try and work out what they want for their water services. Some of the drawings are absolutely horrible, and you wonder how they ever get the buildings put up right.

      It took me 2 hours yesterday working through the drawings to get some information that I could have got in 10 minutes if they'd done a proper schematic as well. Also, if you've got a schematic to look at first the drawings (which are necessarily complex, messy and confusing) are a lot easier to understand afterwards. Admittedly it also didn't help that they'd split each floor into 3 separate sheets, and because it went round a corner at one end, had used a blank space on one sheet to put the corner bit, rather than continue onto a fourth. [shudder]

      There are 2 extractor fans, 2' apart, in the kitchen in my flat. My working theory is that the architect drew the ducting in the wrong place (or it was built wrong) and it was easier to just retrofit a new fan in the ceiling than to connect up the extractor above the hob...

  15. Anonymous Coward
    Anonymous Coward

    For my own opinion, after often thinking the same thoughts as the article writer, is that bad source code is so prevalent because we (humans) can afford to have it that way. Nobody likes bad code. But as long as the thing works to some tolerant degree without crashing (1 crash per 1000 uses) there is no real problem.

    When a Civil Engineer builds a bridge that falls down once every thousand times somebody tries to cross it, he'll lose his job. But software isn't so critical. And when it is that critical it is preferrable (subjective but seriously) to use languages that will catch the programmer doing something stupid at compile time (i.e. ADA), instead of languages that let you get away with anything until runtime when your program crashes into a wall.

    So in the end it's a subtle cost/benefit analysis that is subconsciously going on in developers/managers/testers heads. Spend an extra year making a crash proof program doesn't have a lot of benefit if you count the loss of sales, increase payroll etc. Software can afford to have crap code, because usually the cost of a crash is negligible as long as it isn't crashing too often.

    I've worked in teams where we all tried to write perfect, brilliant, clean (Java) code. Only to end up months behind schedule, and to be honest, no obvious benefit, to the final product. Yeah, hardly any crashes or glitches, but if we coded sloppily the crash rate would have still been "tolerable". Sad but true.

    1. Anonymous Coward
      Flame

      Oh Reallyz ??

      You must be the engineering manager of LSE by now. Their business has been eaten by Eurex and I know why.

  16. Anonymous Coward
    Thumb Up

    Ohhhh... this sounds all too familiar

    Especially the 'I'm teh awesome' part involving C++ gurus and their templates. Templates have their place, but without comments to say what they do, it's a puzzle for the poor sods who follow on behind.

    Which is why I liberally sprinkle comments in my code, even if I believe the code is fairly self-explanatory.

  17. Charlie Clark Silver badge

    Poor examples of bad code

    IIRC APL made extensive use of mathematical symbols which made a good choice for implementing algebra. This is a niche area and requires excellent maths skills but that does not in itself make it a bad programming language. Database work would benefit significantly from set algebra literals.

    Similar with Haskell which, while I don't really like the syntax, is extremely well-suited to certain problem domains and as a purely functional language does this with a lot fewer compromises than trying to do the same in a general purpose language.

    1. P_0

      Re: Poor examples of bad code

      APL and it's daughter, J. (Both were creations of Iverson) have their place. As you said, they are great for algebra and Math modelling. Aside from math and just thinking outside the box, they don't have much use, IMHO.

    2. Anonymous Coward
      Anonymous Coward

      Re: Poor examples of bad code

      We're using Haskell to develop an embedded language, and it is the perfect tool for the job.

      Using a piece of obfuscated code as an example of any language is really not a fair representation of that language.

  18. Mage Silver badge
    Mushroom

    The budget isn't the problem

    Maybe 9 out of 10 "high graded" Graduate "Programmers" (there is not just Comp Sci) can't really program well and never will be good programmers.

    You can easily teach a computer language, has anyone ever discovered how to teach programming.

    Comments are a double edged sword, they can become out of sync with code and most people are worse at good comments than programming.

    A decent description of the function, what the parameters are, what the valid range for each is and what the function returns/modifies is of course a minimum. Include tests on each parameter, even if for performance they have to be commented out on final production version.

    1. Christian Berger

      Re: The budget isn't the problem

      Well you would need to do it the way you teach creative writing. You can learn this to some extend. Writers say that one important part of learning how to write is to have read a lot of good works.

      The problem with many programmers is that they have never seen well designed systems. They grew up on DOS, then had Windows. Even though the underlying ideas inside the kernel may have their beauty and elegance, the userspace does not. The result are software packages storing their configuration in SQL databases or binary files, because the developer never saw how else it could be done.

      Take a look at the unixoid world, you see how it could be. You have lots of small simple tools, each one taking text as input and giving text as output. Suddenly you have a whole system of building blocks. You can integrate those blocks to create cool new systems. That way you can learn to segment your program into different parts, each one doing one thing right. It's a powerful way to approach program design.

      For example I recently had to do some firmware for some networked devices. I eventually ended up making something very unix-like. It simply provided the most bang for the buck, as I was able to design orthogonal tools for the job, and those tools can be re-used. That way you could have one script file containing the parameters (e.g. station number) and the other file deriving the lower level settings from those settings (e.g. IP-address) or things like the CPU_ID.

      Of course as with any art form, there are multiple ideas on how to do it. So feel free to disagree.

      1. Anonymous Coward
        Stop

        Re: The budget isn't the problem

        Sometimes the unixoid "little filter" approach works. It does not work if you have to massage massive data sets in shortest possible time. It does not work properly(efficiently) for things like database or web servers.

  19. Anonymous Coward
    Anonymous Coward

    a lot

    of the bad coders I've come across, and I've seen a lot, just don't seem to know any better. It may be a legacy of my company, but historically they were of the opinion that you took a vaguely technical person, sent them on a basic programming language course, and you had yourself a developer.

    A lot of the people don't think in the right way, they don't have the faintest idea how to properly structure things, or how they should be done.

    Some of the worst are the ones who've been doing it for 20 years, don't have the faintest idea, and just keep on doing what they've always done without ever learning.

    1. Christian Berger

      Re: a lot

      I have seen that problem in multiple companies so far. It is very common.

  20. plrndl

    The root cause of this situation is that IT decisions are being made by people who are not qualified to make such decisions, as they have zero understandoing of the consequences (eg Windows for Warships http://www.wired.com/science/discoveries/news/1998/07/13987).

    This is exacerbated by the problem most technically gifted people have with communicating with the technically illiterate. Thus the marketing and money men tend to heard more clearly.

  21. drekka

    My personal experience of years of "Cleaning" other people's code is that 49% is straight out bad design/code and people coding above their skill level. The next 49% is the poor slobs who get lumbered with the code being either too lazy to fix it, or not willing to call a spade a spade when talking to management.

    I've lost track of the number of projects I've had dumped on me only to find undocumented, unreadable, over-engineered piles of poo. Some I've thrown out and re-written, some I've patched into shape over time.

    The real skill is knowing that in a lot of cases, you can fix a lot of things by deleting more code than you will write.

    1. John Smith 19 Gold badge
      Unhappy

      "The real skill is knowing that in a lot of cases, you can fix a lot of things by deleting more code than you will write."

      Absolutely correct.

      I had a data load program to update which read a lot of different record types from a file. The record type was a field of each record in a standard location within the record.

      A near perfect use of a CASE statement.

      The original programmer did it with a near impenetrable (and unmodifiable) IF/THEN/ELSE/IF/THEN/ELSE chain instead.

      He did not seem aware the language supported a CASE statement.

      1. Christian Berger

        Hmm, I know this is just nitpicking, but the better solution might have been to have a table listing the types of records and what to do with them (for example function pointers to functions processing them, or some other description) and then have a tiny little parser just looking up the record type and doing what the table says.

        That way you can not only trivially change things, but you can even automatically sanity check your table if you wish to. If you document your table and or make it obvious, people will be able to easily edit it.

        There is a point for if/then/else/if though: case is yet another language feature, and the more language features you use the harder it becomes to read for a beginner. Properly made if/then/else/if chains can be just as maintainable if properly made.

      2. Anonymous Coward
        Anonymous Coward

        There are sometimes very good reasons for using IF THEN ELSE statements instead of CASE ones... ;-)

    2. Anonymous Coward
      Anonymous Coward

      maths

      And the last 5% is basic math errors!

  22. Kubla Cant

    Recognise this picture?

    The team contains ten developers. Skill and experience in the team follows a Pareto distribution: two have extensive experience and strong skills, two are very talented but have limited experience, two are people who went into software engineering because they thought it was a well-paid job. The remaining four are recent graduates, one of whom seems to have managed to complete a degree and get a programming internship without knowing what ctrl-C and ctrl-V do.

    Any new piece of work is given to team members 3 to 10. Numbers 1 and 2 spend most of their time maintaining the production software. Because it was written by 3 to 10, there is a lot of maintenance to be done, and the code is hard to follow. The rest of the time is spent mentoring the graduates, but this doesn't really make much difference to the overall picture because they move on almost as soon as they know anything.

    Bitter? Me?

Page:

This topic is closed for new posts.

Other stories you might like