24

I have written a lot of software in many different languages, and I've also "written" hardware for use with FPGAs using Verilog and VHDL.

I tend to enjoy writing hardware more than software, and I think one of the primary reasons is that it is possible to write hardware that is "done" and never needs to be modified: you define the interfaces and the functionality, write a test bench, implement the hardware module, then test the heck out of it using a simulator. Then you can rely on that hardware module as a building block to create something bigger and better: if you need to add a feature to that module, you create a second module and add the functionality there. You never throw away the original module since it is working just fine and can still be useful.

One of my main frustrations with software is that it is never "done". There is always another feature to add. Often when adding a feature it introduces a bug somewhere else that was working just fine before. This doesn't happen in hardware as long as the interfaces are not violated.

To be clear, I'm not advocating building one version of something with a list of features and that's it forever: I'm in favor of iterations and multiple releases over time to add new features. I just don't want to poke the code on the left and find a bug on the right, and this seems to happen after adding a new feature.

Is it possible to write software in a similar way in which hardware is "written"? Is there a good software development methodology that allows forward progress to always be made and allows new functionality to be added without needing to rewrite existing code and introduce new bugs?

8
  • 8
    You seem to be describing OCP
    – Oded
    Jan 20, 2012 at 8:32
  • 2
    @NathanFarrington: Most design patterns (as described by GOF) are following the OCP. One example would be the Template Method Pattern.
    – Spoike
    Jan 20, 2012 at 9:20
  • 2
    @NathanFarrington The open-closed principle is a common principle used by software developers when designing software.
    – Jesper
    Jan 20, 2012 at 14:32
  • 1
    Good question. I think this has long been the goal of software engineering, and successive paradigms have promised to achieve this, but failed to deliver to a greater or lesser degree. If you look at libraries provided with most programming languages they are made up of reusable components. Similarly OOP was supposed to deliver reusable components that could be assembled into software. Also look at Component-Based software, etc Jan 20, 2012 at 21:10
  • 1
    I would imagine that many of the programs we used today are made using bits and pieces which are carbon copies to code that was written 20 years ago.
    – Mallow
    Jan 20, 2012 at 21:23

11 Answers 11

17

Maybe it has something to do with well-defined interfaces and testing, like hardware?

Exactly my thoughts!

Well-designed modules with clear interfaces tend to be essentially perfect. Think of something like String class of Java. It is a computer program, but it has a crystal-clear interface. There are no known bugs in it. It does what it is supposed to do, perfectly. Sure, it has been extensively tested during the last 15 years, and since virtually all programs use Strings as basic building blocks, any bug in it would be quickly noticed. Any "quirks" - not strictly bugs, but design details worth being aware of - such as those described here http://www.jwz.org/doc/java.html are well-known by now, and thus can be taken into account.

Buggy software is partly a cultural issue: people are used to buggy software, and unlike hardware, software can usually be easily fixed afterwards, so it doesn't have to perfect initially (or ever, because hey, we must ship now, let's fix it in the next version). But for a large part, it is a real complexity issue: software complexity has been steadily increasing for the last 50 years or so, but the human brain is the same. When the growing difficulty of achieving perfection and the growing ease of fixing things later on (fast, automatic builds, internet distribution) combine with schedule pressure and lack of discipline, the result is what it is.

Is there a good software development methodology that allows forward progress to always be made and allows new functionality to be added without needing to rewrite existing code and introduce new bugs?

No silver bullet probably, but a good combination of best practices, including but not limited to:

  • Simple, autonomous modules. In other words, low coupling and high cohesion.
  • Immutability. Particularly important with growing concurrency.

It's worth noting that both points aim at reducing complexity. That's the key point. The entropy always tends to increase, and unless we fight that, we'll soon drown in complexity. It's also interesting to see that during the last few years, programming languages have been evolving toward encouraging or even enforcing the above mentioned practices. Particularly, the rise of functional languages is just that: pure functions always return the same value for the same input, there's no state in them. Then you just compose pure functions that take and return immutable values, and limit the inevitable mutability to small well-defined places instead of spreading it all around. Check this out: http://clojure.org/state

11
  • 1
    jwz does not quite agree that the String class is bugfree - jwz.org/doc/java.html
    – user1249
    Jan 20, 2012 at 9:11
  • 1
    It may work as designed, so the question is if the underlying design is broken. I agree, however, that String is a very reliable class.
    – user1249
    Jan 20, 2012 at 9:56
  • 1
    Excellent answer. I now code almost exclusively in Python and try to take advantage of the functional programming constructs. Yes, I think you are right that immutability is key. The first time I create a software module, even if I test it, then I probably messed up the interface, or maybe it has the wrong responsibility or too many. So I make a second module and leave the first one alone! I can still use the first module in the future if I wish, but never ever change it, because although it is not perfect, it works. So functional languages with immutability could help like you suggest. Jan 20, 2012 at 13:23
  • 1
    @JoonasPulakka: Yeah, if there is a one-line summary of software, it might be "there's always another bug". :-) And I think that's one of Nathan's points. Jan 22, 2012 at 13:53
  • 1
    Joonas, you win. I've started learning Clojure. It looks like the best way to make programming fun again. Feb 17, 2012 at 0:52
10

The difference is just how much easier and cheaper it is to modify software compared to hardware. If hardware was as easy and cheap to modify and ship to customers, they would.

I think if I could sum up my poorly-expressed question it would be something like, "how can I have more fun writing software by not introducing bugs into working code and always making forward progress?" Maybe it has something to do with well-defined interfaces and testing, like hardware?

You should definitely check test-driven development.

5
  • Many answers to my question seem to contain folklore. Is it necessarily easier to find and fix software bugs than to design the bugs away from the start? How much does it really cost to modify software? Probably no one really knows. Regarding test-driven development, it makes sense to test software that can be reused. The software then becomes an actual building block rather than a ball of mud. But it doesn't make sense to test software if it is just going to change tomorrow. I guess I was wondering if we can actually make software from building blocks rather than mud. Jan 20, 2012 at 13:16
  • 1
    @NathanFarrington: I believe there's a huge difference in how the hardware/software specification and design is made. Most hardware manufacturers have most likely better specifications for what they make than a software developer whose client can only say "I want a program that does this!" It's pretty much guaranteed to get new features and what not.
    – RCE
    Jan 20, 2012 at 14:21
  • Sure if there are a lot of changes, some tests might need changing too but it's not like the software changes from word processor to web server. Your "new document" feature will still create a new function and its tests should still be valid.
    – RCE
    Jan 20, 2012 at 14:25
  • You pointed out another key principle: the better the specifications are the less change is required down the road. But this was not exactly what I was getting at in my question because you can add features to hardware before its done just like software. I think OCP was the real answer, bad sadly it is a comment so I can't mark it as the answer. Another point was to always make forward progress and I think that TDD can help this by providing regression tests. Jan 20, 2012 at 18:48
  • 1
    Changing software seems easier and less expensive than hardware, but is it really? Yes, I can go make a change and pretty much instantly make a new build with the push of my finger. However, the build still needs to go through validation/QA. What was the opportunity cost? What would I have been doing instead of fixing this bug. What would QA have been doing had they not needed to re-validate the software. Was a different projects pushed off to get this fix to market? There are lot a "hidden" costs people don't think about. It might be easier, but not necessarily less expensive.
    – Pemdas
    Jan 20, 2012 at 20:26
6

I will comment on some of your remarks, hoping you get the answer from these comments.

One of my main frustrations with software is that it is never "done".

This is because the solution specification is incomplete or because the plan to deliver enhancements is not accurate. This can happen to any project software, hardware or any other.

Is there a good software development methodology that allows forward progress to always be made and allows new functionality to be added without needing to rewrite existing code and introduce new bugs?

Of course, creating independent modules should greatly reduce dependency. This has to be considered when you are designing the software. You need to consider, separation of concerns, layers, tiers, controller objects, interfaces, etc.

"how can I have more fun writing software by not introducing bugs into working code and always making forward progress?"

1-Understand requirements carefully. May be you need to consider closing the requirements before design. If you do iterative development, there is no chance of doing this. Some methodologies encourage this, but personally, I think this is not good for all project types. Building software on solid requirements allows for better design.

2-Make your user understand this long-tern philosophy.

3-Plan implementation carefully

4-Design before code.

5-Use generic design when appropriate.

6-Use prototypes as a design confirmation tool.

1
  • These are all excellent pieces of advice. To summarize my thoughts up to this point: (1) make releases a BIG DEAL and do lots of testing and QA before a release, (2) make modules a BIG DEAL and make sure they have well-defined documented interfaces with tests for those interfaces and assertions to see if the interface is violated and once a module is "released" it is never modified (OCP). Jan 20, 2012 at 13:03
5

As people are usually very quick to point out, one of the benefits of software is that it is supposed to be easy and relatively cheap to change compared to hardware. This is especially important when your realize late that you got something fundamentally wrong. Do the same with hardware, and you lose a million dollars, so as you've said, you use your simulators etc, and you test the bazinga out of it. This I think is where the paradigm fails somewhat when you shift to software.

Get into the average software developer's head, and what you have is a very busy person with an incredibly tight deadline. His manager is saying that it's ok to leave a few bugs because you can always fix it later. Tests are often an afterthought, but even in a test-driven scenario, the tests are kept minimal and the code written to the minimum of the tests, and often shortcuts are taken so that many of the boundary cases might be missed. The system may be thoroughly unit tested yet is rarely tested rigorously as a whole, and just as rarely stress tested to any great degree. Add to this that you write software from scratch, and there is little opportunity to simulate the software before you commit to writing it, primarily because we rarely write software from the same sort of fine-grained building blocks that you would find in hardware. Taken from this perspective - which I don't believe is too far fetched an example - the software is not tested to the same high degree that hardware would be, because realistically it can't be, and to attempt to do so would not be seent to be very cost effective.

Back to the OP's question. Could you define a system of building blocks from which to derive all of your software? Possibly. Would it be very cost effective? Probably not, because by the time you get around to developing a robust enough system of components, tests, and other paraphernalia to support this ideal system of programming, you would find that your competition would have already beaten you to the market, and even worse, from the average programmer's perspective you would probably find a "cookie-cutter" style of programming system to be very limiting and more likely very boring. I personally work on an API, where the bulk of the module code has been refined and standardized so completely, that all I do now is generate a code template and fill in the blanks. Most of my time can be spent writing simple connector code, and getting the modules out the door as fast as possible. It's seriously mind-numbing. There is very little opportunity to do more than just code the same sorts of things over and over, so when another project opportunity comes along, I jump at the chance to be able to do ANYTHING else.

So how can you get to deliver high quality and well factored software, and yet actually enjoy yourself doing it? I believe this comes down to your choice of tools and methodology. For me the answer has been to employ the use of a good BDD API, because it has allowed me to create very easy to read, yet highly granular code. I can build a suite of tests out of a minimal number of reusable methods, and describe my tests in the language of the specifications. In this way, I come close to a more component-ized development approach, except for the fact that I am responsible for designing and checking the building blocks. Additionally, the test output pinpoints the exact part of the test where the failure occurs, so that I don't have to guess whether the failures are in the setup or the assertion. This means I spend more of my time focused on solving problems instead of chasing them, my time is used more efficiently, I get more work done more quickly, and I can move on to other interesting tasks more quickly.

Tuning your methodology also helps. I'm a big advocate for applying lean development principals, and combining them with many of the other techniques and principals that the Agile movement has been banging-on about for many years now. Having eliminated most of the wasteful practices that I used to find so frustrating has helped a great deal to make development a more enjoyable activity. I'm still left with the issue that sometimes - but hopefully not too often - bugs will appear in my code, however I now find myself with more time, and even more incentive to spend more time writing more robust tests, and aiming for 100% test coverage. Even better, it feels really great to see all of those green lights appear at the end of my day, and be able to provide a complete - automatically generated - test report to give to my boss at the end of the day.

2
  • I'm curious, you write that testing is important but also mind-numbing. Jan 20, 2012 at 13:06
  • @NathanFarrington Thanks for pointing that out. My point was to talk positively about the testing, but I was thinking about that while typing something else, so it came out totally wrong in that paragraph! I've corrected to suit the actual point I was trying to illuminate!
    – S.Robins
    Jan 20, 2012 at 22:07
4

One of my main frustrations with software is that it is never "done". There is always another feature to add.

If that frustrates you, consider a different career. Seriously.

The point of software is to be able to add features. The entire reason for inventing "software" in the first place was so that we could add features.

Often when adding a feature it introduces a bug somewhere else that was working just fine before.

That's a QA problem.

This doesn't happen in hardware as long as the interfaces are not violated.

That's also true in software.

Is there a good software development methodology that allows forward progress to always be made and allows new functionality to be added without needing to rewrite existing code and introduce new bugs?

Yes. You have to actually practice Quality Assurance.

6
  • 1
    I'm not trying to troll by the way, and yes maybe I'm not cut out for software. But you say the "point of software is to be able to add features." Is it really? von Neumann invented software to be able to build a computer that could compute more than one mathematical function without rewiring its logical and arithmetic units. I'm curious where this software-feature philosophy comes from. Hardware has features to, but the purpose of hardware is not to add features. Jan 20, 2012 at 12:25
  • I assume by Quality Assurance you mean testing. Yes, my intuition says that extensive testing is required to produce quality software. But I think it goes beyond that. In hardware, a module might have a bug in it. But when you add new hardware, it does not introduce new bugs into an existing hardware module. Eventually all the bugs in all the modules can be found and fixed. But in software, often code is changed (modules are changed) rather than added, which can introduce bugs. I guess I was looking for a software development methodology that was purely additive. Jan 20, 2012 at 12:29
  • I now have a more intelligent comment on your answer. The reason I griped about software never being "done" is probably because I've always used a very loosy-goosy approach to releases. One new feature equals next release, with no regression testing and very little QA. If releases were a BIG DEAL then I bet my gripes about software never being done would go away. Jan 20, 2012 at 12:44
  • @NathanFarrington: Turing invented software to break the ever-changing Enigma codes. "by Quality Assurance you mean testing". False. I mean Quality Assurance -- every aspect of development should have quality standards which should be met. Testing is one (limited) way to assess the quality of one kind of artifact. "code is changed ... which can introduce bugs". Correct. That's a failure of quality assurance -- not an inherent feature of software.
    – S.Lott
    Jan 20, 2012 at 15:29
  • 1
    Later computers built by Turing did. Keep digging. Keep an open mind, however. If you are just looking to refute my position, you won't learn as much as if you consider that Software is called "soft" because the point was to change it and evolve it as the need arose.
    – S.Lott
    Jan 20, 2012 at 19:00
2

it is possible to write hardware that is "done" and never needs to be modified: you define the interfaces and the functionality, write a test bench, implement the hardware module, then test the heck out of it using a simulator. Then you can rely on that hardware module as a building block to create something bigger and better: if you need to add a feature to that module, you create a second module and add the functionality there. You never throw away the original module since it is working just fine and can still be useful.

In the software world, we call that "module" a library, and we use it just the same way. Many libraries are built to the point that they function well, and then sit contentedly doing their jobs with no change until Something Important leads to the next revision. Think of them as software that's been potted with epoxy :-)

One of my main frustrations with software is that it is never "done". There is always another feature to add. Often when adding a feature it introduces a bug somewhere else that was working just fine before. This doesn't happen in hardware as long as the interfaces are not violated.

Hogwash. Perhaps you're personally better than many other non-soldering-iron "hardware" people, but I've seen any number of bad circuit designs, failing chips (e.g., the famous Intel "f00f" problem), but that doesn't speak to the field as a whole. And as faux-hard ware becomes "softer", the problems become more difficult to prevent.

Is it possible to write software in a similar way in which hardware is "written"? Is there a good software development methodology that allows forward progress to always be made and allows new functionality to be added without needing to rewrite existing code and introduce new bugs?

Yes. We just don't use those methodologies much. They tend to be extremely expensive to operate, and most programmers don't enjoy working within their restrictions. But when human life is involved, for example, well, yeah, we try not to kill the users.

One last point: Software has a different financial model from hardware, even programmed hardware. Most non-consumer software, and some consumer software also, is sold in a manner that encourages change. When you can tell a business "Pay us $10,000 now plus 18% a year", you can essentially re-sell the product every few years. But to justify that fee, you need to give the customer the changes they want. Hmm ... thinking of Apple's hardware-obsolescence curve, perhaps this isn't a difference after all - hardware just makes you really re-buy it!

1
  • Never said I was better than anyone. ;-) When hardware has a bug, it becomes news. When software has a bug, ummm, wait software always has bugs. Which methodologies are the ones we don't use because they are too expensive and not fun? Jan 21, 2012 at 0:24
2

Is it possible to write software in a similar way in which hardware is "written"? Is there a good software development methodology that allows forward progress to always be made and allows new functionality to be added without needing to rewrite existing code and introduce new bugs?

I recommend you look into "formal methods" for verifying the correctness of designs and software. The simulator tools you use for hardware design are trying to do something close. I do not believe that tools for formal methods are anywhere close to being useful in industry at this time, and the only industries that have strong incentives to be defect-free are avionics and medicine (interestingly enough, the FDA clearly says "software is different from hardware" on that link). Furthermore, if you are developing with Verilog/VHDL, then you are sticking with binary logic. This drastically reduces complexity. There isn't going to be a hardware equivalent to a Y2K issue.

The big problem is that things are complex. And you cannot eliminate complexity, you can only move it around.

1

I would also point out that software bugs in hardware can often kill people. So there is more care taken to thoroughly determine the requirements and extensively test them up front. And those requirements dont need to change until the hardware does. And since new hardware may require a rewrite, I suspect the cruft doesn't build up so much either.

Business requirements on the other hand change constantly, sometimes you can hardly get one requirement into production before a change is asked for. Sometimes, I've had the requirement change multiple times before it does get to production. This is a result of several things. First the project stakeholder on the business side is often less interested in spending the time to thoroughly define what he wants because he is "busy" and "important" and people won't die and the families sue him or get him thrown in jail if he blows off his part of the process. Second, the project stakeholders tend to actually have a better idea of what they want the hardware to do as it is less abstract to them. They really don't know what they want until they see it. Which is less of aproblem with Hardware.

1
  • You have a valid point. The types of things that we traditionally make in hardware are well understood: processors, USB controllers, PCI Express endpoints, memory controllers, etc. Then we push all of that application business logic into software. Maybe as we work our way up the software stack things get messier and less well understood? Jan 21, 2012 at 0:29
0

how can I have more fun writing software by not introducing bugs into working code and always making forward progress?

I would love to find a final answer to your question. But the reality is that there is no easy way to do it, thats why extreme programming and TDD technics are becoming so popular. You need to embrace change, because is going to happen. I dont know if its funnier this way, but a lot less stressful for sure ;-)

http://en.wikipedia.org/wiki/Extreme_Programming

When you interact with hardware, the hardware needs x value and thats all (in theory), but when you interact with people today they need x, and tomorrow they can need y, etc. It is how it is, bussines and people requirements change. Because Persons != machines, so code that NEVER changes most of the times is not possible.

Also as i said on my previous/deleted answer, try to avoid changes that are not important by making people think before start coding. Involve users more on decision making, etc. Clarify costs of changes, plan more, etc. Those are not "ways of coding", are ways of "not coding" because with more though about requirements there will be less changes and more fun.

2
  • 1
    Good answer. I've done Extreme Programming. It seems like the exact opposite of what I was looking for, where the entire project direction can change weekly depending on the whim of the customer. I'm not against iterative releases, I just don't want the 2nd version to introduce bugs that were not present in the 1st version. And you are right that up-front design can save effort in the long run. Jan 20, 2012 at 12:37
  • As i always say, the best code, is no code. :-)
    – H27studio
    Jan 20, 2012 at 16:34
0

Is it possible to write software in a similar way?

Yes, it is. Just be as careful as if you are developing hardware, test everything you can, and your software will be of similar quality.

by the way, haven't you heard of HW bugs? Much nastier then any SW bug and harder to fix (not just upgrading the software)

2
  • 1
    Yes hardware has bugs too, even mature well-tested hardware like processors. Good hardware is designed so that the hardware bugs can be fixed in software! The reason for my original question is that I have written lots of software but I've always been dismayed over how easy it is to introduce bugs and how messy the system as a whole was. I'm a clean-cut kind of person so the hardware development methodology always seemed more natural to me. It might also have something to do with scope. Jan 20, 2012 at 12:15
  • 1
    @NathanFarrington The software is usually more complex then a HW. HW is tested more thoroughly. SW can change easier, therefore people tend not to pay as much attention. Jan 20, 2012 at 12:33
-1

There are high level tools with lots finished 'bricks', as you call them, that you can combine into applications. The bricks are finished pieces for you to use, you just have to combine them together. Maybe you think that's easier... until your customer asks you for some weird and unexpected changes.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.