Monday, July 20, 2009

How To Cut Corners in Software Development

I have recently been put on a project that has a fixed and tight timeline and a fixed scope. As many of you know, there are 3 aspects of software development: time (or budget), scope, and quality. Two are set by the powers that be and the 3rd will vary accordingly. In our project, the time and scope have been set which means quality must be sacrificed.

I’ve been thinking of the best way to develop under these circumstances. Unless we mutiny and refuse to build the software, my teammates and I must find ways to cut corners, without ending up producing a bug ridden, un-maintainable, useless piece of sh…software.

I believe developing under these circumstances will not only produce poor results across every standard that we judge software by, but also be more expensive. Maintenance costs make up most of the expense of many systems. In general, the higher the quality produced during development, the lower the maintenance costs, and the lower the total cost of the system.

Usually changes to a system decrease its overall quality. The quality starts at its peak when the system is first created, and degrades overtime as more and more changes are made. That isn’t always the case and there are many other contributing factors, but it has been my experience it’s the general trend of many software systems.

It doesn’t have to happen. Usually the approach during maintenance is to touch as little as possible. If however the maintenance programmers take the attitude “I’m going to improve the code every time I touch it,” then the trend is reversed. Maintenance is more expensive initially, but doesn’t degrade the quality. As a result, it should make the system cheaper overall by extending the life of the system.

The best approach when cutting corners during development is to push the work off until maintenance. This requires the maintainers take the latter attitude.

There are 2 problems with this. First, the maintainers rarely take the attitude to improve the quality of the system, and their managers would rarely support it (budget wise) if they did. The second problem is that it is always more time consuming and expensive to make changes in the maintenance phase then it is in the development phase.

In the best of circumstances, cutting corners during development is like taking a loan with a huge interest rate to be paid during maintenance. Default on the loan and the system goes bankrupt.

Design


Always think before you code. That part of design hasn’t changed.

The goal is to be quick, without being too dirty. What is “too dirty”? That is a judgment call made on a case-by-case basis.

As with all software projects, every design principle is useful, but there are a few that deserves special mention:

KISS (Keep It Simple Stupid): Remember the goal is to fix many of the problems during maintenance. The simpler the code is the easier it will be to do so.

DRY (Don’t Repeat Yourself): Rewriting the same code over and over is not only bad design, but can also be time consuming. However, sometimes cut and paste can be quicker and simpler. Use your own judgment on how and when to apply this principle.

Encapsulation: Encapsulation is always important, but much more in systems developed quickly. It is inevitable that mistakes and lapses in judgment will be made. The result is likely to be a mess. The more isolated that mess is, the less harm it can do, and the easier it will be to fix. Lack of adequate encapsulation will likely mean a failed project.

Testing


Unit testing is essential. If it is done well it can save time, especially during later phases of the project. It will also make cleaning up the code during maintenance much easier and cheaper. It’s well worth the initial effort.

Don’t try to hit some magic code coverage number. Instead focus on tests that give the biggest bang for the buck. Make sure the most complex logic is isolated and test them thoroughly. It is likely most bugs and changes will be in those classes.

Don’t write a test class for every class. Instead group classes together and test them as a single unit. There is a fine line between unit and integration tests. It is judgment call as to how close to that line is appropriate. Another option is to forgo unit testing in favor of small integration tests.

Consider not testing some boundaries. It can be very time consuming to unit test code that interacts with frameworks (especially web frameworks). Instead isolate that code as much as possible and keep it simple. Delegate all responsibilities to other classes that are more testable.

Bugs


Even under the most ideal circumstances, developing code quickly will result in many bugs. How do you deal with them when there is not adequate time?

Finding all bugs can be time consuming. It many cases you should trust your testers to find your bugs. All changes should be system tested before passing to the QA team, but minor changes do not always warrant a full system test. Again use your best judgment.

Second, not all bugs are created equal. There are many bugs that can be put off. Such bugs can be time consuming to fix as well. By a show of hands how many of you web developers have spent several hours or more messing with the CSS to get an element to align exactly the way you want it?

Scope


If you hired a contractor for $5000 to build a deck, and through no fault of his own, ran out of money after completing 90% of it, what would you do? Most people would pay the extra money to finish it. Most business owners would do the same for their software.

Don’t feel you have to do whatever it takes to deliver on time. In general it is more important to deliver adequate (if not top notch) quality late, then really poor software on time.

Expectations


Quickly developed software will inevitably lead to a poorer user experience and higher maintenance costs. It is important the project stakeholders are well informed of the trade offs being made by the budget and timelines. The worst outcome is to push a development staff hard for months to deliver a poor quality product and then get told, “If we had known this is what was going to happen we would have given you more time”.

*ails


The grails and rails frameworks have the potential to change the rules entirely. The above was written under the assumption software has a large up front cost, and is under maintenance for a long time. What would happen if development of small and medium sized web applications become so quick and cheap that it becomes advantageous to rewrite it every couple of years?

At the beginning of the post I mentioned how the quality of software starts out at its highest level and degrades over time. If the system is going to be rewritten in a couple of years, then the quality only has to be good enough to last until then. It may be better to aim for quicker development with lower (but still adequate) quality.

There are still a lot of question marks about how grails and rails will be utilized in the corporate world, and the impact that will have on software development. The only thing I’m sure of at this point is that there will be an impact.

Conclusion


I have yet to finish the project I’m on. One of the reasons I’m putting these ideas out there now is to elicit useful feedback.

Feel free to tell me your ideas and point to any study that contradicts or confirms anything I mentioned above. Is there anything that I missed? Is there anything that I got dead wrong? Also leave your rating, either +1 Joss Whedon (good) or +1 Aaron Spelling (bad).

Blog Directory Listing

Saturday, February 21, 2009

What Is Encapsulation? Part II: A Library That Does Nothing Is Nothing

OO design is built on several layers of encapsulation. You combine statements and encapsulate them in a method. You then combine methods and encapsulate them in a class. The next layer is a component, then a library, and finally the system itself.

Each layer should encapsulate more and more details. It should only expose the absolute essentials to the next layer. A library should therefore encapsulate all (or nearly all) of its details behind a well-defined, well-documented, simple, public API. A library that doesn’t encapsulate its details isn’t a library. At best it is simply shared code, at worst it is a disaster waiting to happen.

I have lately seen libraries that contain nothing but interfaces and classes with only data (essentially data structures). Usually these libraries come in pairs. One provides the public well defined API, and the other provides the implementation. This makes the two co-dependent on each other. They should both be thought of as one cohesive library.

There are good uses of this pattern. JDBC for example provides an interface library that is part of a specification. Multiple vendors provide their own implementations. Client code uses only the interfaces and the classes defined by the spec. This allows code that uses JDBC to stay independent of each vendor.

In general both the API and implementation should be a single cohesive library. This will allow the code to evolve together as a single unit, and be easier to change, deploy, and manage.

That isn’t to say libraries shouldn’t use interfaces. One could effectively argue for exposing all functionality via an interface. I think that is overkill. Still, using interfaces to expose most functionality is certainly a good idea, but put those interfaces in the same library as the implementation unless there is a specific technical reason why they can’t be deployed together. Even then both should be thought of as a single library forced apart by circumstances.

A while back I had an unpleasant experience with Charter Communications. They would schedule an appointment to install cable, but nobody would show up. This happened 5 times in a row before I gave up and moved to another provider.

The core of the problem is that the customer center is a completely separate department from the service center that did the installations. Some information was shared, but there were no real direct lines of communication. Everyone I talked to on the phone tried to be helpful, but they were limited in what they could do.

Here loose coupling worked against them. The service department implemented the requests gathered from the customers by the customer service department. There is a natural cohesion there, and decoupling them made it hard to provide adequate service to their customers.

In software terms, the customer service library contains the interface for making requests (such as an installation), and contains data used by both the customer and the service library. The service library implemented the interfaces, and consumed the data to do the actual work.

Suppose the service library needs to be modified to provide additional information or provide better interaction with the customer for some edge cases. It would be difficult to do since the customer service is in a different library with its own release cycle, evolution, and possibly entirely different development team. It’s likely one or both of two things will happen. First, users will be allowed to bypass the customer service library and use the service library directly. Second, the service library just won’t provide that behavior, or won’t do an adequate job. Neither option is appealing.

Loose coupling is a good trait to have, but only when it is combined with high cohesion. Separating a cohesive unit into two parts will at best force both parts to evolve together making the separation between them artificial. At worst it will allow the 2 to diverge making it impossible to add new features with out significant effort. It’s true with departments in a large company and its true with software libraries.


Blog Directory Listing

Saturday, February 7, 2009

What Is Encapsulation? Part I: Private Fields Does Not Encapsulation Make

Ever since hibernate came onto the scene a few years ago, I have been seeing more domain entities that contain nothing but getters and setters. This seems to go against the core principle of object oriented programming: data and behavior should be encapsulated into a cohesive object that exposes a well-defined interface.

Making fields private does not make the class encapsulated. If a domain entity contains nothing but data, then it is closer to a data structure then to an object. Such a design has a definite procedural under tone to it.

Procedural programming may fit your problem better then OO, but usually it is a mistake, and therefore an anti-pattern. It has even been given a name: Anemic Domain Model. See Martin Fowler’s explanation: http://www.martinfowler.com/bliki/AnemicDomainModel.html.

There are other benefits then the one Fowler mentions. First it is easier to share core business logic when it is encapsulated in a domain model. Take a company that sells custom T-shirts and hats. The core logic around an order is unlikely to change between the web application for customers and the in house order taking application. Encapsulating it behind an Order object makes it much easier to share between the applications.

This is in theory only. In practice the in house system is likely to be on a different platform then the web application, making it difficult to share the domain model. This is the core problem SOA is trying to solve.

The second problem is that business rules are often different between applications. You may need more strict validation on an order placed over the web by a customer then you would on an order placed by a trained staff member.

Patio Building As Software Design


I’m hiring a contractor to build a patio at my house. It’s going to have a concrete floor and two stonewalls. The contractor is handling the details of how the patio is built. I don’t need to know where to get the materials, how to get them on site, how to build the walls, or how to pour the concrete.

The contractor will delegate to the suppliers who will ship the materials, the workers to lay the stones for the walls, and the concrete guys to pour the floor. The contractor will need to coordinate between them as well. The workers will need the correct materials before they can start, and the concrete can’t be poured before the walls are built.

The contractor is encapsulating me from the details of the patio building process, and is coordinating the different domain experts in order to bring them together to successfully build my patio. He doesn’t need to know the details of how to make or ship the materials, how to build the walls, or how to pour the concrete. The experts in those respective steps are encapsulating him from the details.

If this was a software system, below might be a model for it:



I make a request (build a patio) to the contractor (the service layer). The contractor delegates to several experts (domain entities) such as supplier, workers, and concrete guys. The contractor coordinates the work as the experts perform their respective steps.

Since the contractor delegates’ the responsibility to create the materials, the supplier can make changes to their manufacturing process (by upgrading equipment for example) without the contractor needing to know. This allows the supplier to easily use the same or similar (subclassed if you will) manufacturing process to create materials for other contractors, which may or may not be building something completely different (a new multi-story building perhaps). The supplier can make any change necessary in order to better accommodate the needs of all of its customers.

Multi-Tier Architecutre


I’ve seen the anemic data model anti-pattern recently in multi-tier architectures. A client and remote server run in different contexts. It is difficult to share any behavior in that scenario. This is the reason why DTO (Data Transfer Objects) were invented. The Anemic Domain Model anti-pattern comes from applying the DTO pattern to hibernate persisted entities.

Usually the hibernate objects are themselves passed to the client. This makes putting logic in them difficult to do, since most of it can’t run on the client. Under these circumstances, keeping most of the logic in the DAO (Data Access Object) layer becomes the path of least resistance. The DAO is a subset of the service layer, and is exactly the kind of problem Fowler warns against. It forces a more procedural design on your architecture. Fowler and many other OO advocates strongly advise against multi-tier architectures for this very reason.

Don’t go overboard!


A perfectly encapsulated class would not allow any access to any data it contains, nor would it send the data to any other objects it collaborates with. This is neigh impossible in the real world, unless you have one object that does everything. I, however, wouldn’t recommend that kind of design.

OO is a theory. Applying that theory in the real world means compromising. It is important to understand the theory, the benefits it brings, and how those benefits are derived. When compromising on the principles of OO, it is important to know what you are giving up. Don’t stick to dogma of any particular paradigm if the consequences out weight the benefits.

Java is built to be an OO language. That doesn’t mean procedural concepts can’t or shouldn’t be applied. The consequences will be higher however. OO should be the first choice. The consequences of any deviation should be weighed carefully.

Conclusion


You may have noticed that i didn't give a solution to the anemic domain model anti-pattern in multi-tier environments. I'm more curious about what you think. Is there a good way of avoiding this problem? What is it?

Also leave your rating for this post: +1 Ted Neward (brilliant) or +1 Britney Spears (stupid)

Blog Directory Listing

Saturday, January 24, 2009

Hibernate's Identity Crisis

I ran into an issue with equals and hascode methods for hibernate persisted entities the other day. That got me thinking of the proper definition for the identity of entities. Most of the implementations I dealt with used the reflection-based tools from apache commons. Using reflection for the identity of an object is a bad idea in all but a few scenarios. So I started thinking of a better solution.

The first thing that needs to be decided is what defines the identity of a persisted entity? There are 3 choices:

1. Object Instance. Each object instance is unique. This is the default java implementation.
2. Business Key. Fields are chosen to define the identity of the object.
3. Primary Key. The primary key on the related database table is used.

With the object instance definition, every object that gets created is different then all other objects, even when the share the same data and represent the same row in the database.

Most (but not all) entities have a natural key: one or more fields that make that entity unique. An employee has a social security number. A magazine has a name and publication date. These fields are usually separate then the primary key, especially when hibernate is used. Under this scenario, 2 entities can share the same identity and represent 2 different rows in the database.

Using the primary key will ensure consistency between the identity of an object and the identity of the database row backing that object. The exception to this is when it has not yet been assigned a primary key.

At first glance, the definition based on the primary key is the correct one from a semantic point of view. However, there are scenarios in which the other 2 definitions make more sense.

Before choosing which definition to use, the implementation consequences should be taken into account. The definition will define how equals, hashcode, and in some cases compareTo should be implemented. If two objects have the same identity then equals should return true, hashcode should return the same value, and compareTo should return 0. The implementations of equals and hashcode must be consistent with each other. There is more leeway with compareTo.

The conversation on hibernate forums explains many different implementations and consequences here: http://forum.hibernate.org/viewtopic.php?t=928172&postdays=0&postorder=asc&start=0&sid=e34c88ebf24b90219be45fbc81752084

Strictly from a semantic point of view, the object instance definition is the least useful, but the easiest to implement (since it’s the default in java) and has the least implementation consequences.

The business key definition seems to have lost favor recently. It does offer better semantics then object identity, but is harder to implement (although some IDE’s can generate it for you).

Identity based on the primary key is the best from a semantic point of view, but has the most implementation consequences. Consider this example. A Person object is created. The id defaults to a value (usually null or 0). This is the first problem. If another Person object is created, it too will have the default id value. The 2 Person objects will then be considered equal, even if they represent 2 different people and will eventually be stored in 2 different database rows.

The second problem is the objects identifier will be generated and assigned when the object is stored. The java libraries assume the identity of an object is based on immutable fields, and therefore doesn’t change. If 2 objects are considered equal, then they will remain equal for the life of both objects. Since the id changes, this isn't true. More importantly, the hashcode will return different values.

The implications of this are mostly confined to HashSets and HashMaps. When an object is stored in either collection, a hashcode is generated. This value is used to assign the object to a bucket. When the id changes, so does the hashcode and bucket. If the object is looked up afterwards, it can’t be found.

The link above gave 3 solutions:
1. Switch to another identity definition.
2. Use a different identity definition if equals or hashcode are called before the object is persisted. It will need to store the definition used to ensure consistency for its life. A variation of the same definition can also be used.
3. Generate the id when the object is created. If there is a version field, hibernate can use it to distinguish between insert and update operations. If there isn’t a version field, the application must ensure the correct operation is used.

I’ll add two more options. First, don’t use HasSets. A List can be used instead in most cases. The application can ensure there are no duplicates in the list.

Second, have the HashSets and HashMaps rehash itself if the entities it contains change by implementing the observable pattern with HashMaps. When the object changes its identifier, it should notify the containing HashMap. The HashMap then rehashes that object.

Conclusion


There is no right answer. Which definition and implementation you use depends on the context. The best solution is to use the java default implementation (the object identity definition) as the default. If another definition is needed for a specific entity, then it can be overridden. The business key definition is the next best solution, since it is easier to implement.

What do you think? Are there other definitions? Should everyone always use the primary key definition? Don't forget to rate the post +1 Martin Fowler (brilliant) or +1 Paris Hilton (stupid).

Blog Directory Listing

Sunday, January 11, 2009

The importants of complete testing

System testing has been implemented since basically the beginning of time. Now days anyone that doesn’t do unit testing isn’t considered a professional programmer. I won’t waste my time trying to convince you that testing is important. What I want to talk about is the importants of all types of testing: unit, integration, system, performance, and hostile.

Almost everyone does unit testing during development, system testing during QA, and occasionally performance testing just before production. Integration testing typically gets dropped, and hostile testing isn’t even on anyone’s radar. That is a mistake. All are essential to high quality software.

Unit Testing


At its core a unit test is a functional test for a unit of code. Consider building a bridge. Like software, it is built by first creating pieces: the foundation, the support pillars, the suspension, etc. The pieces are then assembled to create the bridge. In software, the code units are the pieces. Once they are completed, you assemble them to create the system.

As you’re assembling the pieces, it would behoove you to check each piece before assembly of the bridge. The last thing that you want is to build the bridge and have it fall down because of a flaw in the support pillars. It would be better to test the pillars at the beginning of constructions to ensure they will hold enough weight. If they don’t, you still have time to correct the flaw, or adjust your design.

It also protects you against sudden changes in the pieces. If you need to switch out the concrete for the foundation, you can apply the same test to the new concrete to ensure it matches your needs.

Keep in mind that the weight you expect may be wrong because of a flaw in your design. In such cases, the tests of the support pillars will pass, but the bridge will still fall down. Unit tests by themselves are not complete. You can’t rely on them alone.

Good OO dictates that a unit of code encapsulates data and behavior. A good unit test will ensure that piece of code correctly manipulates the data independent of the context in which it will be used.

A good unit test for a foundation pillar in a bridge is to ensure it can support weight up to 10,000 lbs. You may be interested in using the pillars for a smaller bridge that only weighs 2,000 lbs. per pillar. The unit test at 10,000 lbs. will give you confidence your pillars will work for similar, but larger bridges.

Your code is an independent API. Pretend many developers will use it in every system in the world. Then design your unit tests. Don’t get in the mindset of testing the code only in the context you intend to use it for. The context will almost certainly change many times before the code reaches the end of its life. Ensuring each unit of code works in any context will make maintenance much easier.

Consider testing a custom implementation of java.util.List. You may just need the get and add methods for the system you are writing the code for. To be complete, you should implement all the methods properly. You should also test every method. You should also test adding nulls, or other edge cases even if they are impossible in the context in which the new List will be used. The code using the List may change, or it may be reused in another system.

There are numerous other benefits to unit testing. I’ll allow the unit test zealots to make those points. I will add one more thing. Most everyone considers a unit to be one class. That may not necessarily be the most efficient use of unit testing. Many classes we write are there to tie stuff together, which sometimes results in small classes and methods. Consider expanding the scope of “unit” until you have a group of classes with interesting behavior to test.

For example, in my current project we have a service layer and a DAO layer. Often the implementation of a service method is just to call the same method on the DAO layer. In such cases it might be more efficient to write one unit test that test both instead of two separate tests.

Integration Testing


Many developers don’t do integration testing with the excuse that it duplicates either the system test or the unit tests. They are wrong. The difference is in the focus. Unit tests focus on the functionality of a unit of code. System tests focus on the functionality of the system as a whole. Integration tests focus on the interaction between 2 or more units, components, or systems.

Often the tests exercise the same code, but the pass criteria is different. It isn’t the intention of the integration test to ensure that component A or component B is functioning properly. It should only check that component A and component B communicate with each other properly.

Consider the bridge that we are building. It would be a good idea to check that the support pillar can properly attach to the frame, and the connection can support the expected weight. The benefit is to find flaws in how the components fit together before the bridge is built and falls down. The fact that the integration test also tests the pillar and the support frame is incidental. The focus is to test how well the 2 pieces fit together.

Automated integration tests have many of the same benefits that automated unit tests have. It too can ensure bugs are caught early during development, and help provide a safety net when doing refactoring. The difference is in the kind of bugs caught.

System Test


System test ensures the system as a whole functions properly. System testing our bridge will ensure it won’t fall down when a car drives across it.

Everyone should understand what system testing is and its benefits. I won’t explain it further. I would, however, like to add a bit more. First, agile development has changed the scope of system testing, and the job of the system tester.

Back in the day, testers job started when the developers completed the system. Ideally the testers would only need to regression test the system only a hand full of times as developers fix all the bugs. In most cases it wasn’t worth the effort to automate those tests. Agile has changed that. Now the system needs to be tested a few times every few weeks. Agile development methodology has made automated system testing necessary.

This has changed the job of the testers from clicking buttons on the screen to writing scripts that click the buttons on the screen. Testers now need to be programmers. In exchange, they get a new title: QA Engineer.

I’m all for keeping metrics on system testing, but keeping track of the number of bugs found doesn't tell you anything about the quality of your system test process. It is like keeping track of the number of bad drugs the FDA keeps off the market, or the number of lead based toys are caught before making it to Wal-Mart. A much more interesting statistic is the number of dangerous toys being sold, or the number of bugs found in production. In other words, look at the number of bugs missed in system testing. Which is better, a system test that finds 1000 bugs and allows 100 into production, or one that finds 100 and allows 0 into production? The number of bugs found says more about the development process then the testing process.

Performance Testing


Like the other types of testing, performance testing has a unique focus. The pass/fail criteria should be related to the number of calls that can be made, and/or the lag time of each call.

The scope, however, can very. You can write a performance test at a unit level to ensure a particular algorithm is efficient. You can write one at the integration level to ensure a service or external call will be fast enough. More popular though, is to do performance testing at the system level to ensure the it has the performance and scalability required.

In the context of our bridge, performance testing would be placing weight on a pillar or the foundation until it fails, or more likely, it could be placing weight on the completed bridge to ensure it won’t fail when it is full of cars.

Remember the idiom: premature optimization is the root of all evil. That does not translate into: premature performance testing is the root of all evil. If you have tight performance, memory, or scalability goals, it may be worthwhile to do performance testing at a unit and integration levels during development, and include those tests with other automated unit and integration tests. Be smart about this. It may not be necessary to test everything, and additional tests can be added once a performance problem is found.

You should not optimize the code until it fails to meet your system wide goals. Wait until the system is put together and do system level testing. If it fails then, you can use the unit and/or integration level tests while your doing your optimizations. You can then tighten your goals on them to ensure future changes don’t hinder the performance.

I will spare you the details of how to do proper system level performance testing. There are numerous resources that you can find on that intraweb thingy.

Hostile Testing


The focus for this testing is on what happens when things go wrong. I don’t mean testing expected errors. That should be defined in the requirements and covered by the system tests. I mean unexpected errors.

If your system retrieves customer information from an external system, then retrieving a non-existent customer is a system test. Testing what happens when the external system doesn’t return at all is a hostile test.

Consider the bridge. A hostile test is ensuring the bridge can withstand high winds, earth quakes, car crashes, boat crashes, etc. The purpose is to ensure the bridge does not collapse under these circumstances. That would take an already bad situation and make it many times worse.

Likewise hostile testing on software systems is done to ensure one failure doesn’t cascade into a much bigger disaster. A failure in one system should not cause any system that communicates with it to fail also, or at least they should fail in a controlled manner.

Hostile testing is usually geared towards communications with external systems (including databases, web servers, etc). The external systems will often need to be mocked, which is easier to do at an integration level then a system level. However, it isn't that hard to mock external systems at a socket level if your just going to drop the connection, or return random data.

Michael Nygard has explained hostile testing in greater detail. I would highly recommend looking through his blogs and articles and picking up his book Release It!

Conclusion


Most development shops concentrate on system testing, and unit testing. Some even do performance testing. Testing is an important part of any development effort. It needs to be as complete as possible.

The fact that integration, performance, and hostile testing is time consuming and hard is no excuse. If you skip any of them, you simply do not know what will happen. Weigh that risk against the cost of testing. Is it still too expensive?

What do you think? Are all forms of testing worthwhile in every case? Is there any other forms of testing that i missed? Please leave your rating +1 Martin Fowler (brilliant) or +1 Paris Hilton (stupid).

Blog Directory Listing

Saturday, January 3, 2009

GUI Design And The 3 Types Of Users

GUIs are one of the most overlooked part of a system, and one of the most important. Everything we do as developers ultimately boils down to empowering users. Nothing can have a larger impact to that goal then the software that the user actually interacts with.

So what makes the best GUI? It depends on the task at hand, or more importantly, the qualities that are most important to the task: speed, accuracy, simplicity, etc.

There are 3 general types of users: the customer, the worker, and the techie.

The Customer


The customer is your average person that comes to your system (usually a website now days) and wants to accomplish a specific task, such as to place an order or look up some piece of information. The thing that distinguish customers from other types of users is that you have no control over them. You can't force them to go through training before using your system, nor would it be a good idea if you could.

Consider when you go to the DMV. Do you know which documents are required for every type of license? How would you feel if the clerks at the counter expected you to know every detail of how to renew your license with little instructions? If your anything like me you just want them to tell you what documents they need and how to obtain them, so you can get them and get your license as easily as possible. Customers feel the same way when they use your system.

This requires the GUI to be as simple as possible. Simple for the user that is, which means it needs to be complicated for you. The GUI should guide the user through the task in a simple way and completed with as little thought as possible. Any unnecessary details should be removed. The GUI should make liberal use of wizards to help break the task into small steps. Each field should clearly explain what it is, what values are valid, and how to go about getting the information (if it isn’t obvious). There should be no abbreviations. The GUI should validate everything as much and as soon as possible. You should consider removing some functionality in order to keep the GUI simple, or at the very least isolating the more complicated rarely used functionality from the rest of the interface.

Worker


The worker is someone that uses the system on a regular basis in order to achieve a repetitive task. Think data entry clerks, customer service representatives, or many other types of office personnel. Workers have 2 advantages over customers when in comes to system interaction. First they have access to training and support, and second they use the system on a daily basis. Both mean they will be capable of learning the details of the GUI, which allows it to be much more complicated.

The best example of interfaces for workers is game GUIs. Good games have a depth that makes it very complicated. Any player will appreciate being exposed to the details of the game play. It may make the learning curve high initially, but to get good at the game, it is required. Still, it isn’t the goal of the player to learn the intricate workings of the game. The goal or task is to beat his/her opponent. Good game GUIs will allow the user to concentrate on winning the game. It will expose details that help with that goal and hide those that do not. It is the same as good GUIs for all workers.

The most important qualities for GUIs for these types of users are speed and accuracy. The GUI should allow the user to enter all the data needed as quickly as possible, while maintaining the integrity of the data and the system. It should also allow users to quickly correct mistakes. The screens can contain a lot more information then GUIs for customers. To make room, the fields should contain less help and context information. Usually a (possibly abbreviated) label will be enough.

Another important aspect of good worker GUIs is performance. The screens should not only allow the user to enter information quickly, but also to store and retrieve it quickly. If you create a GUI that allows customer service personnel to place orders twice as quickly as before, then you just doubled the capacity of your company (and possibly got half of your users fired, but try not to think too heavily on that).

When designing a GUI for workers its important to remember that they are experts at their jobs, not at the system. The GUI (and the system) should be designed to empower them to do their job better, faster, and more accurately. It should not distract them from their job by forcing them to learn non-relevant details. Just like a game should not force you to know how it stores its information, or how it displays its graphics in order to be good at it.

Techie


The last type of user is the techie. The hallmark of the techie is his/her intimate knowledge of the system. Examples are system admins working with operating systems, or developers writing a GUI to manipulate the system he/she helped develop or maintain.

The most important qualities for these GUIs are simplicity and power. Things that would be an error for other users should be allowed with a warning at most. In this situation, the user knows more about the effect of what he/she is doing then the GUI. An excessive amount of validation will prevent users from manipulating the system in a way that may be needed as a result of bugs or other problems. It will also most likley be a waste of time to code. I’m not saying the GUI should allow anything bad to happen to the system, just that the definition of bad is relative to the user.

The first thing you should consider when developing a GUI for a techie is if it is really required. There are numerous tools available that may suffice. I would even consider using SQL directly to the database (with limited permissions of course) instead of developing a GUI on top of a table that effectively does the same thing. For java developers JMX is another way of avoiding developing your own GUI.

Conclusion


Keep in mind that these are general types of users. Rarely will users and systems be that simple. Think of it as a line. On the left, with the democrats, we have the customers that need as much guidance as possible. On the right, hanging with the republicans, we have the techies, who require all the power and none of the simplicity. And the independents in the middle are the workers, with some knowledge of the system, but no details.

Where your users fall on this line depends on the system, your users, and the context. For example, your GUI could be for a job that has a high churn rate. In which case, your users would be workers, but fall further towards the customer end of the spectrum. Or you could have a GUI designed for the help desk. In which case they would clearly be on the right, but fall further towards the worker end. It’s also possible to have systems that doesn’t fall cleanly at any point on the line, and have to take aspects of all 3.

What do you think? Are there any other types of users that i'm missing? Do you have any other advice for GUI design? Should we start to rewrite all the GUIs in the world, or was this a waste of your time? Feel free to leave your opinion in the comments by rating the article a +1 brilliant or +1 stupid!

Blog Directory Listing