Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

Make it good

by radiantmatrix (Parson)
on Oct 18, 2004 at 15:34 UTC ( [id://400178]=perlmeditation: print w/replies, xml ) Need Help??

Above all other concerns to a quality developer is making the software good. Of course, opinions differ as to what constitutes "good" software -- but, there are a few commonalities, ways to make it good:

  • Make it Work
    Software that doesn't work is bad software. Period. Software that works conforms to the design and/or specification, and behaves in predictable ways. This is obvious, but I'd be remiss if I didn't mention its importance. <Update>Of course, while conforming to the design is important, all is for naught if the software doesn't actually run. As FoxtrotUniform and jplindstrom note, sometimes making it work means getting something out the door to the users.</Update>
  • Make it Clean
    The term "clean" can be interpreted a number of ways -- the important thing is that your interpretation fits the realities of your project. Code should be well-abstracted: keep an appropriate separation between method and data. Make sure that you have the rights to any code you reuse. Move code to modules when appropriate. Handle errors, rather than dying.
  • Make it Secure
    Insecure software is rarely useful except as a "throwaway". Be aware of and avoid common security mistakes (like passing formats to printf as user-supplied variables). Respect file locks and permissions, even if you can ignore them. Play nice! Handle errors in a sane way.
  • Make it Friendly
    "Friendly" means different things, depending on context. Whoever will be using and/or administering your final product should find it easy to figure out. Make things intuitive when you can, but remember that not everyone will agree on what "intuitive is". Document well. Keep the interfaces logical and consistent. Don't assume user input will always be valid.
  • Make it Reusable
    There is very little code that will only ever be used once. Move oft-used code into modules. Code with reusability in mind: use constants and globals sparingly, and try not to "code yourself into a corner". Reusable means modifiable.
  • Make it Readable
    Chances are, someday someone else might have to maintain your code. Be sure to use appropriate in-code documentation. Use POD. TMTOWTDI, but be consistent. Structure similar tasks in similar ways. Use indentation, and do it in a consistent manner. Let the flow of your code reflect, visually, the flow of the algorithm.

Of course, as will all "rules", there are times to bend -- and even to break -- the above. Know your project, and let that knowledge guide you.

Update:


As hardburn helped to point out, these should not be construed as "absolute". Compromises have to be made, and made on a regular basis -- that's part of the development cycle. These aren't really "rules" but goals to reach for while coding. It should also be noted that these aren't in any particular order of preference, they are in the order I wrote them down, and that is all.

Also, as with most admonitions, these can be taken too far (except maybe "make it work"). Excessive thrift becomes miserliness; so excessive attention to security, portability, or any number of the other goals can become a poison in its own right.

As dragonchild points out, the first rule, Make it Work, is by far the most important. Lose that one, and you just wasted your time on the others.


Is there anything (else) I missed, or got wrong?

radiantmatrix
require General::Disclaimer;
"Users are evil. All users are evil. Do not trust them. Perl specifically offers the -T switch because it knows users are evil." - japhy

Replies are listed 'Best First'.
Re: Make it good
by BrowserUk (Patriarch) on Oct 18, 2004 at 16:42 UTC

    I think there is a counterpoint to this argument. If you look at the history of software projects that went bad, the most consistent reason for failure is that they tried too hard to make things perfect. To cover all the bases. To be all things to all people. To encapsulate everything. To control everything. To handle every contingency, edge case and possibility.

    As a freind of mine once put regarding a project that cost close to a billion dollars and ultimately went nowhere... "Designed to death!".

    The art of software (design/code/maintanence) is as much about what to leave out, what not to handle, what not to encapsulate, and what not to fix, refine or refactor, as it is about doing those things.

    I agree with your first point. Make it work. But I do not think that you gave it enough emphasis (despite putting it first) nor enough importance. It is better to produce something that does something--even if it does it wrong--than to produce nothing at all.

    More is learnt from making a mistake and correcting it than from trying to avoid the mistake in the first place. Intellectual 'what-ifs' and 'maybes' are interesting games to play, but ultimately less productive than "It does"' or "It doesn't."s.

    Like the proverbial picture, one line of code is worth 1000 words. Even if the result is that the line of code is thrown away, the the learning (experience) from having written and tried it is invaluable.

    The earlier mistakes, mis-assumptions and bad design are found, the earlier they can be rectified and the less their effect upon the project enddate.

    And the quickest and most relaible way to find mistakes is to make them.

    Unnecessary security is more than just unecessary--it is a drain on resources, both when coding the program, and when using it.

    Designing in reusability, before there is an application for reuse, besides being a waste of effort if the code is never reused, frequently leads to design and coding descisions that only serve the purpose of the assumed reusability.

    These decisions are often non-optimal for both the original application, and when a real application for reuse comes along, non-optimal for that too.

    Better to code the solution to the problem at hand, and redesign/refactor for reuse, only once the nature of the reuse is known and can be factored into the design.

    To do otherwise places constraints and costs that rarely (IME) ever produce payback.


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
    "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon

      First, let me say that I don't necessarily disagree with you. But, I'm not talking about "design first, code later" mentality. Yes, things can be designed to death, but with the above goals in mind both while you design and while you code (which are really the same step, but that's another Meditation), you can find the balance.

      The art of software (design/code/maintanence) is as much about what to leave out, what not to handle, what not to encapsulate, and what not to fix, refine or refactor, as it is about doing those things.

      This, to me, falls in the category of Make it clean. Part of being clean means not doing the unneccessary.

      Unnecessary security is more than just unecessary--it is a drain on resources, both when coding the program, and when using it.

      There is really no such thing as "unnecessary security". It's possible that someone might do something unnecessary in the name of security. But, such things are actually not good security -- the illusion of security reduces security, as does needless complication from unnecessary measures. It's important to remember that the principles above work together -- you can't put all your eggs in making it secure if doing so would cause it not to be clean (or worse, not to work).

      Designing in reusability, before there is an application for reuse, besides being a waste of effort if the code is never reused, frequently leads to design and coding descisions that only serve the purpose of the assumed reusability.

      There is a big difference between designing-in reusability and coding with reusability in mind. The former, as you've said, is a bad idea unless reusability is one of your goals. However, it is a good idea to avoid practices that needlessly break reusability. Again, by using the principles of "make it work" and "make it clean", it's apparent that there are cases where reusability is less of a concern. That's why I phrased that section the way I did.

      Better to code the solution to the problem at hand, and redesign/refactor for reuse

      And this is a task made easier by keeping in mind potential reusability during coding. "Oh, I shouldn't make that a constant, what if I need to reuse this?" Of course, sometimes making it work precludes this -- like when performance trumps most other concerns.

      radiantmatrix
      require General::Disclaimer;
      "Users are evil. All users are evil. Do not trust them. Perl specifically offers the -T switch because it knows users are evil." - japhy

        I followed dragonchild's earlier link to Extreme Perl--Preface and came across the quoted text below. It (with the addition of a little context) does so much a better job of saying what I was trying to say above, I just wanted to point it out.

        Start out by

        ...do[ing] the simplest thing that could possibly work...
        and then--if required--make it (slightly) more complex in order to make it work. Iterate.

        I sort of arrived at this basic philosophy for coding (which I attempted to describe in My number 1 tip for developers.), over many years almost by accident. It is basically the result of an adverse reaction to having been forced to attempt to follow one new "programming paradigm", "structured programming methodology" after another as each new fad came (and for the most part went).

        From the classical Waterfall Model, through SSADM/LBMS, OOA/OOD, DataFlow Modelling and Use Cases to the current trends for Data Driven and Test Driven development, AOP and XP. Each of these brought something slightly new and valuable to the subject.

        The problem is that all too often, that one new thing is seen (by some, temporarially) as the be-all and end-all of chique and they try to design a whole "methodology" around it. The problem for the programmer is pursuading management that whilst that one new pin is a useful technique, that it can be incorporated into the existing working practices without throwing everything else away.


        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "Think for yourself!" - Abigail
        "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon
      I agree with your first point. Make it work. But I do not think that you gave it enough emphasis (despite putting it first) nor enough importance. It is better to produce something that does something--even if it does it wrong--than to produce nothing at all.
      So was Matt's Script Archive a good thing in the long run?

        I said produce something. Not produce it and then promote it to the general public.

        I did start to try and objectively try and consider whether anything had come out of Matt's script's that might be comsidered good. But then I realised that it would be pointless. Your question has nothing to do with what I said in the post to which you replied. Neither in isolation, nor in the context of the thread of which it is a part.

        At this point, I am going to invoke Godwin's Law, as in my opinion, any reference to Matt's script's in the context of Perl programming is exactly equivalent.


        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "Think for yourself!" - Abigail
        "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon
        Yes, I think it was. I learnt perl by fixing bugs in his code. I expect many others did too.
Re: Make it good
by dragonchild (Archbishop) on Oct 18, 2004 at 15:58 UTC
    While all of these are nice to have, it is critical to remember that the only criterion of business software is the first one - Make it Work. 2/3 of all software projects fail. I would hazard that the main reason this happens is they forget the first rule.

    OpenSource, incidentally, tends to avoid this problem with the "Release Early, Release Often" mantra.

    Being right, does not endow the right to be rude; politeness costs nothing.
    Being unknowing, is not the same as being stupid.
    Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
    Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

      Well, there's also "deliver it on time" and "deliver it within budget". Following the other guidelines helps in achieving all three business criteria.
        Does it matter if I deliver something on-time and under-budget if it doesn't work? This implies that there is a definite ordering to the rules.

        Being right, does not endow the right to be rude; politeness costs nothing.
        Being unknowing, is not the same as being stupid.
        Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
        Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

Re: Make it good
by hardburn (Abbot) on Oct 18, 2004 at 16:47 UTC

    A nice list of rules, but what really makes software hard is when the rules end up being contridictory.

    For example, for security reasons (rule #3), I never want to see a plaintext password in a database. Instead, I use SHA1 hashes with salt. The problem is that users forget their passwords, and when they come to ask us, we have no way of getting the orginal (thus breaking usability, rule #4). We have to set the password to something else and give them that. IMnHO, this is perfectly reasonable, and the random password we give them is probably more secure than whatever they were using before (their dog's name, favorite food, etc.). As far as I'm concerned, users who don't like this are themselves a security hole. Management doesn't agree, so now it's plaintext passwords everywhere.

    As a side (and off-topic) note, I'm convicinced that password authentication is a failure. I read a book by a conviced cracker written about 15 years ago (sorry, I don't remember the name, but I do remember that it was (ironicially enough) published by the Microsoft Press). He said the biggest security hole is your own users, and highly emphisized the need to train your people, especially with regards to passwords. If we haven't succeeded in doing this by now, I don't think we ever will.

    Unfortunately, password authentication is often the only practical option at this point.

    "There is no shame in being self-taught, only in not trying to learn in the first place." -- Atrus, Myst: The Book of D'ni.

      You make an interesting point, and one I will probably have to incorporate into the node. These rules could easily contradict each other -- except that they are not absolute rules.

      In your cited example, you have to decide whether security or usability is more important in that particular case. Rules like these don't remove the power and responsibility of making choices, they just provide a framework to guide those choices.

      Also, this is arguably a bad example: it is secure, so it hits rule #3. It's also usable, so long as the password reset capability is easy for the support staff to use. The idea behind these rules is that they are flexible enough to fit your specific circumstances.

      radiantmatrix
      require General::Disclaimer;
      "Users are evil. All users are evil. Do not trust them. Perl specifically offers the -T switch because it knows users are evil." - japhy
Re: Make it good
by FoxtrotUniform (Prior) on Oct 18, 2004 at 20:55 UTC

    You missed the most important one: Make it run.

    A program that meets 90% of its spec and takes a week to write is often more useful than one that meets 99% of its spec ("there is always one more bug") and takes three months to write. Basically, what I'm saying here is that your program should always work to some degree, so that you can release them to the users, get feedback on how you're doing (real-world specs are moving targets), and get part of the problem solved right now. If it's buggy, so what? Make sure your users know that what you're releasing isn't "finished", and make sure they know that you're listening to the bug reports.

    I'm thinking of this mostly from the perspective of the tools I used to write in industry: data-munging scripts to save people hours of drudge work. In almost every case, I could get a large chunk of the problem solved in a matter of hours, and start saving my users time right away while working on the rest of the feature list. On the other hand, I think this works well in most other application domains, too. Let's say you're writing a dynamic, user-tracking website for tracking local bands and gigs. Don't wait until you have theme support and a spiffy forum to release the first version; let people start listing (and advertising for) gigs right away while you add to the feature set.

    We'd all be pretty disappointed if vroom had waited until Recently active threads was implemented before going public with Perl Monks.

    --
    Yours in pedantry,
    F o x t r o t U n i f o r m

    "Lines of code don't matter as long as I'm not writing them." -- merlyn

      I didn't really mean to suggest that one should wait until all these goals are met perfectly before releasing anything. In fact, I'm pretty sure it's impossible to create the "perfect code". But, what you release should work, at least reliably enough for your target users. Beta releases are somewhat exempt -- that's why they're beta. :)

      The development process is, of course, iterative. And the mantra of "release early, release often" is definately valuable in the quest toward good software. You do have a valid point about not waiting until you implement 100% of the features before a release. However, I did say "conforms to the spec" -- what you do implement needs to be as correct as possible. In many cases, you can conform to a spec without implementing every feature of it. In some cases, though, you can't.

      radiantmatrix
      require General::Disclaimer;
      "Users are evil. All users are evil. Do not trust them. Perl specifically offers the -T switch because it knows users are evil." - japhy
        I didn't really mean to suggest that one should wait until all these goals are met perfectly before releasing anything.

        I don't think you did. All I'm saying is that you didn't point out "make it run". I'm not attacking your position, just adding to it.

        But, what you release should work, at least reliably enough for your target users.

        (Emphasis added.) Here's my point: don't get so hung up on technical correctness that you forget about why you're writing the program in the first place. If your target users can get more work done with incomplete, beta software than they can without any software at all, give them the beta while you work on the next release.

        --
        Yours in pedantry,
        F o x t r o t U n i f o r m

        "Lines of code don't matter as long as I'm not writing them." -- merlyn

      It entirely depends on what you are writing. I guess if your MP3 player is silent for a second every 10 second (meaning it works 90% of the time) that's ok. But given the amount of traffic lights I need to take en route to work, I'd have two or three accidents a week if they only worked correctly 99% of the time.
Re: Make it good
by SpanishInquisition (Pilgrim) on Oct 18, 2004 at 19:22 UTC
    One point here is that while programmers want to do many of the above things, management often has unreasonable schedules and ships products too soon, or does not allocate sufficient resources -- hence -- we end up with code that does not work, or else you have people you work with writing code that doesn't work.

    Hence, the reality is, make your OSS/Free/For-Fun projects not suck so at least you can remember what it is like to write good code...

Re: Make it good
by tilly (Archbishop) on Oct 18, 2004 at 16:56 UTC
    Yes, we would like to do those things.

    Did you have any advice on how to actually accomplish them?

      You do: Random thoughts on programming

      radiantmatrix
      require General::Disclaimer;
      "Users are evil. All users are evil. Do not trust them. Perl specifically offers the -T switch because it knows users are evil." - japhy
Re: Make it good
by CloneArmyCommander (Friar) on Oct 18, 2004 at 16:06 UTC
    What about a certain OS, that will remain nameless, that meets none of the criteria ;) (I say this jokingly :).
      Why is this joking? I can only assume you're referring to Windows, which really doesn't meet those criteria.

      - apotheon
      CopyWrite Chad Perrin
        I depend on Windows at school, my luck is if I submit something saying it absolutely does not meet any of the criteria, Windows would throw the blue screen of death at me :), hahaha :).

        Neither does Unix or Linux.


        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "Think for yourself!" - Abigail
        "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon
      ...a certain OS...

      Unix? :)


      Examine what is said, not who speaks.
      "Efficiency is intelligent laziness." -David Dunham
      "Think for yourself!" - Abigail
      "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://400178]
Approved by coreolyn
Front-paged by claree0
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others learning in the Monastery: (4)
As of 2024-04-24 05:10 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found