As you may know, I’m self-employed (I do iPhone and Mac OS X development for clients worldwide). Unsurprisingly, this means that I love my clients - because after all, they allow me to make a living doing what I most enjoy. Clients place their trust in us to make their applications and other software a reality. Clients are unquestionably a Good Thing.
However, in this industry in particular, there’s a tendency to talk about our clients in (shall we say) unsympathetic terms. There’s often a knowledge gap between ourselves and our clients (which is presumably partly why the client is using our services in the first place), and there’s a sort of world-weary and slightly mean-spirited habit of characterising clients as stupid because of how that knowledge-gap manifests itself.
The thing is, we’re all somebody’s “dumb client”. We encounter knowledgeable professionals in many fields all through our lives, and for the most part we lack very detailed knowledge in any field but our own. Things which are trivial and obvious to others are unknown to us, and by no means just a matter of common sense.
That’s all fine and fair, and perfectly normal. It’s true for almost every field of endeavour except the one that we specifically have qualifications in, and yet the software industry perhaps has a more prominent “look how dumb our clients are” mentality than others. It’s kind of counterproductive to the goal of getting people to keep giving you money.
Clients’ misunderstandings or incorrect assumptions are usually just based on a lack of expertise. It’s great they have this lack of expertise, because that’s what makes them pay us to complete these tasks for them. That’s something that we want to encourage. So, I’ve put together a list of some common requests made by clients. These are requests which would tend to elicit a “dumb client” response in the industry in general, not because they’re silly or impossible but rather because they involve effort disproportional to the client’s expectations.
We often arrogantly assume this is due to dumbness. But it’s not. In fact, it’s usually perfectly reasonable for the client to believe that less work is required than actually is. There’s information that the client lacks, which changes the picture considerably and gives rise to the actual amount of effort required to implement the request, but the fact that the client doesn’t have that information up-front is not in itself a failing of the client. It’s just part of our expertise, and not something we can or should reasonably expect the client to possess.
In most of these cases the client is entirely justified in thinking that the task will be easy and/or quick. It makes complete sense. Their conclusion remains wrong, but it’s a sensible and justified conclusion from the client’s point of view, thus the facts warrant an explanation of why the reality is different from the (dare I say, logical) expectation. Let’s look at some examples. We’re not going to get technical here; I’m aiming this as much at our clients as at my fellow developers, and I think both can gain something from it.
Can you just add another of those?
This is the basic question behind many mid-project requests which require more effort than the client expects. The actual thing that the client wants more of could be almost anything: another button, another export format, another window with an alternate view of the existing window’s data, and so on. We collectively roll our eyes.
It’s reasonable to think this is easy.
After all, the first thing is already there - it’s a solved problem. All the magic happens in code, and code can be copied and pasted. Entire files of code can be duplicated in moments. There is (pretty much) no arbitrary limit on the amount of code in a file, the number of windows your app puts on screen, the number of files it writes to disk, and so forth. So, making just one more of whatever it is ought to be easy. That’s simplistic, but it’s not irrational. It’s certainly not stupid. It’s just wrong - but wrong in good faith.
The reality is more complex.
It turns out that duplication of code isn’t sufficient for duplicating a feature, or a part of the UI. In many cases, straight duplication of code within the same part of a file of code will actually lead to errors and the inability to even compile the app. There must often be a way to uniquely refer to and/or set up each separate thing within a set of similar things, and this requires writing code to specifically take care of that situation.
If the multiple things are some kind of data in the application, then we have to take account of the fact that there’s now more than one when we save or load that data; it’s not always handled automatically. Changing one button into multiple buttons has an effect on the layout of the window, to say nothing of the fact that the new button must do something, and that functionality must be created. Multiple windows where there was only one before might raise a whole host of issues, including the application having to keep track of them and how they relate to one another, particularly when considering what to do when creating a new document or quitting the app. There are hidden consequences of any expansion of functionality, and there are often a multitude of things we must do to keep the software behaving “the same” or even “sensibly”.
This is all to say nothing of the fact that, if you’re asking for one more of a thing, you might actually be asking for “many” of those things. Will precisely one more actually be sufficient, or could that change in future? If you ask for two now, and we provide that, then you ask for three later on, you’ll expect the additional effort to be even less than when we added the second one (because we’ve done it twice now, so how hard can it possibly be?).
It may very well be the case (and in terms of maintainability of the code it almost certainly will be) that it’s actually cheaper overall to add support for an arbitrary number of things, instead of just the two you need right now. The cost of doing that might be more than a quick patched-up ability to have two things, but it may well be a lot less than the cost of supporting exactly one more right now, then another one or two in a month’s time. This may seem surprising (and I guess it is a bit surprising), but it’s nevertheless the truth.
This one-or-many thing gives rise to a common objection, which brings up a fundamental frustration which software engineers face: the idea that a “properly designed” system will be agnostic to whether you have one thing or many things, and will have been written so that it seamlessly adjusts to either situation without requiring modification. That’s indeed almost always possible, and is also the way that your developer wants to design your software - because we enjoy designing ideal, flexible, intelligent and aesthetically pleasing (in an architectural and visual sense) systems. The reality-check, of course, is time - which equals money (your money, dear client).
There’s a term - “architecture astronaut” - for a software engineer who designs a system according to such a grand and idealised plan that they often fail to deliver the basic functionality within a reasonable timespan, or create something so flexible and generalised that it fails to do the specific things that are required without extensive additional work being done; the idea is that they’re looking at the project from somewhere up in orbit, rather than from a closer and necessarily more pragmatic position.
All software engineers are to some extent architecture astronauts, and wish to design perfect things. We will design your system to support a thousand windows and a billion rows of data and terabytes of files - and it will take as long, and cost as much, as you’d expect of such lofty goals. If you’re willing to pay us to create that wonderful thing, then I invite you to contact me. But you may wish to contact your sponsors or other funding body first, because your business plan is probably unrealistic. What you want is software with certain functionality, delivered within a certain timespan, and that doesn’t allow for a trip up to Earth orbit.
We’ll design and build the software to your specification, and it’ll cost more to change later than to just design it that way in the first place. So, the advice would be: (1) don’t say “one” if you really mean “more than one”, and (2) if the number of things you need is more than one, strongly consider whether you wouldn’t be better off just saying “many”. We’ll do the rest.
Can you make that configurable?
In this situation, we’ve created some functionality, and it works. It makes some assumptions about how it does whatever it does, and the client realises that perhaps these assumptions won’t always hold. Thus, the request is to make this feature configurable - to let the user change how it works if they need to.
It’s reasonable to think this is easy.
The thing that’s seen as “hard” about software functionality is software functionality. It’s making the software actually do the thing. That’s a reasonable position, because we think of software in terms of what it lets us actually do - its purpose in our lives. iTunes is not “that app with the blue list on the left and the bigger list on the right and the controls at the top”; iTunes is the app that lets us manage our music (amongst an increasing variety of other functions).
Once the app does the thing it’s meant to do, letting you tweak a couple of options (like where it actually puts the file, or what color it uses when drawing the border on the printout) is just the icing on the cake - it’s the final 5% of the whole functionality. There’s no additional “understanding” necessary to let you tweak the behaviour of a thing you’ve already brought into existence, because you understand it so intimately. And you have a nice drag-and-drop environment to pull in the checkboxes and such for the configuration UI, too, so this request is going to be a quick one.
The reality is more complex.
The first part of the thinking above is more or less accurate: exposing a certain assumption or setting or parameter isn’t very difficult or time-consuming at all. It’s the second part that’s the big simplification. Adding UI for a setting often isn’t quite as trivial as it would logically seem to be. We need to make sure the new UI is in a logical place in the app, and has meaningful explanatory text (perhaps in several languages). We need to make sure that UI behaves sensibly when its window is resized, and that it checks to see if the user’s input to it is sensible. We also have to make sure that the UI is actually connected to the behind-the-scenes settings it’s meant to control.
That’s just to allow the setting to be changed; there’s more work to be done when the setting actually is changed. We need to store the new value so we behave correctly the next time (we also need to make sure that we’re paying attention to the stored value when the app launches, or when the appropriate functionality is triggered). We also need to ensure that our app’s behaviour changes appropriately if the user modifies the setting, especially if it’s a setting that controls something visual - we’ll probably want to have the app instantly update its appearance in that situation, and that means that the relevant parts of the app have to listen for the setting being changed (perhaps by asking to receive a notification when the setting changes).
That’s not to say that any of this stuff is hard; in fact, each individual part is usually very easy. It’s just that there are a lot of parts, and that they’re each multiplied by the number of new configurable settings needed. Adding a bunch of configurable settings can quickly nickel-and-dime your time-budget (and thus your actual financial budget) to death.
So, consider carefully whether something really needs to be configurable, or whether perhaps a well-chosen default setting will be just fine for 90% of your users. And if you really do need those settings to be configurable, be realistic about the time required to do that.
Can it do X when Y?
This is the infamous special case, where you have one standard behaviour but then you discover that, in a certain specific circumstance, you need the software to behave differently. There can be many reasons for this, some of them as unarguably legitimate to the client as they are intellectually dissatisfying to the developer.
It’s reasonable to think this is easy.
There are a couple of ways you could argue that this task should be easy. The first and most obvious one is that, very often, the special case is only subtly different from the default case - some slight but essential modification in behaviour when certain circumstances arise. Logically, then, the special behaviour is so close to the standard (already coded) behaviour that surely only a small amount of work is necessary. It’s difficult to argue with that.
The second argument stems from the fact that many non-developers have only a hazy concept of how software decides what to do in a given situation, or where there are opportunities to reuse behaviour we’ve already created. For example, if clicking a button does the same thing as choosing a menu-item in your app, and the client asks you to modify what happens when the button is clicked, the client may reasonably wonder if that will also affect the behaviour of the menu-item (in most cases it will indeed also affect the menu-item, since the button and the menu-item will likely both be triggering the exact same piece of coded functionality - not a duplicate, but actually the same one).
Following on from this uncertainty about how functionality is localised, we have the fact that we’re only changing what the software does in one case - just a single situation. Surely, thus, an isolated and straightforward modification with no collateral damage.
The reality is more complex.
Let’s first deal with the initial argument - that the special case is probably only slightly differently from the default case, thus should be easy to support. Intellectually that seems accurate enough, but there’s a wrinkle introduced by the fact that developers like to reuse code as much as possible. We’d rather not simply copy the entire standard case, make modifications to suit the special case, and then have duplicate copies of the parts of the two cases which are in fact identical. That’s unpleasant, and can cause trouble later on (like if we find a bug in one of those identical parts, and fix it in one case but forget to fix it in the other one).
Our preferred approach would be to try to keep only one copy of the pieces which are the same, and just have different code for the truly unique parts. This means breaking apart the default case and figuring out when and where we take a detour for the special case, before rejoining the main path of the code for the next identical part, just like taking a slightly different route when you’re walking home. Doing this “breaking apart” of the default case can require making a number of changes (even before we start to write the code for the special case), to make sure that the code is now generalised enough to support both cases. This can take a little time, and in the worst scenario may require rewriting a sizeable part of the default case - even though it was already working just fine.
That brings us to the second argument: that we’re only changing the behaviour in one situation, thus it must be an isolated change. That’s one way to look at it, but it’s often not a very realistic one. Note how I’ve been referring to the “default” or “standard” behaviour; that was quite deliberate. If the software does the same thing in twenty different situations, it’s almost certain that there aren’t twenty identical pieces of code - there is in fact just one piece of code, which is triggered in each of those twenty situations.
It’s thus not hard to see that adding a special case for one scenario is actually closer to doubling the amount of code (notwithstanding the parts that aren’t different from the standard case, mentioned previously). This perhaps makes the estimates of effort for special cases a little less surprising.
Special cases are tricky. Life often requires them, and there’s a balance to be struck between splitting a special case out into a whole separate part of the app, and just shoe-horning it into the more common behaviour because it’s similar. It’s always a judgement call, and it’s one that your developer can help you to make. Try to work out if you really do have a one-size-fits-all piece of functionality, or whether you might need special behaviour when a given situation arises - and tell your developer about it as soon as possible.
Can you make it reusable?
This is a question which can mean just about anything, and therein lies the danger: it probably means something different to the client than it does to the developer (and perhaps something different to each client or developer).
This request is very commonly made regarding visual parts of the app, including any custom controls/widgets which have been created. The client has paid for these controls and would understandably like to be able to reuse them in other projects later, hence the request. Very reasonable, and indeed surely just professional diligence on the part of the developer in the first place - why ever create a control that isn’t reusable?
It’s reasonable to think this is easy.
This is an interesting request because there are two different levels on which you can assume it’s trivial, and they differ in terms of how technically savvy the client is. A client who knows little about the black art of software development will look at the control and likely see it as quite an abstract thing - a button is just a button after all, regardless of the label or icon it has on it. This is an accurate and sensible belief, and very natural given how many standard buttons and other widgets they see reused in every app on their operating system every day. Thus, controls are known to be inherently reusable by their very nature, and the request should be met with ease.
A more technically-aware client will perhaps know something of the nature of how controls work - perhaps that they create their own appearance based on an internal sort of template, substituting the appropriate label text and other visual elements according to their current configuration. The client may also know, or rightly intuit, that it’s poor software engineering indeed which makes a control so inflexible as to be useless when considered outside its current application (the control might be said to be too tightly coupled to its parent software in this case). Thus it is in fact an expectation of the client that the developer would have created the control in such a way as to facilitate reuse; that this capacity would in fact be an inherent property of the control. I’d likely strongly agree with that proposition.
The reality is more complex.
Some developers reading this will be wondering how I could possibly plan to argue against the idea that controls should be created to be reusable - and indeed, I’m not even going to try. They should be, and that’s pretty much that. The problem is, as stated above, there are many ways to define reusability.
A client with low-to-realistic expectations may define reusability as simply the capacity to have more than one instance of your custom button in the same application, each one with its own label and its own behaviour when clicked. It’s probably not going to be difficult to satisfy that expectation, and we can service the client’s request very quickly indeed.
Other clients have different expectations, however. It’s often the case that the client will define “reusable” in a far wider sense than the developer. The key issue here is that, unless told otherwise, the developer will create a control which is reusable to the extent that is (1) implied by the nature of the control itself, and/or that is (2) implied by the specific use the control has in the current application.
That sounds a little complex, but it’s really not. Taking the example of a custom button that shows a gradient background and a picture or someone from your Address Book, with their name beside the picture, the developer will tend to make that control be reusable in terms of what it is and what it’s being used for. It’s a button, so it will be reusable in the sense of not being tied to a specific behaviour when clicked; it will probably also allow changing its width and perhaps height, and certainly will allow changing its label text. It’s also being specifically used here to show an image and display a gradient background, so it will likely be written such that it supports showing any image (or even none at all), and will probably also allow customising the appearance of the background too.
If this means “reusable” to the client, then there’s no issue. If, however, “reusable” to the client means that the button could also be reused in a situation where it should pop up a list of other people in the user’s Address Book (because it’s already showing one of them, after all), or that is should perhaps expand when clicked to display all relevant information about the person, well then there could be a problem. The client’s definition and expectation of reusability goes beyond what the developer has been informed of or is able to reasonably infer, and it will require time and additional cost to bring those expectations into alignment.
There’s one additional important thing to mention here, which again is related to visual elements such as controls: Interface Builder. Interface Builder is a visual, drag-and-drop UI building tool used to create the user interface for Mac OS X applications (that’s quite a simplification in itself, but it’s accurate enough to be useful here). There are similar visual GUI builders for other application frameworks and other platforms, and this note can apply equally well to those.
The client may quite reasonably expect that making a control “reusable” doesn’t just mean what we discussed above - they may also assume that the control will in fact be accessible from within Interface Builder (or appropriate equivalent), ready for drag-and-drop adding to future projects, visual configuration with Inspector palettes, and all the other conveniences which tend to accompany visual GUI tools.
The unfortunate (and perhaps rightly surprising) fact is that the ability to create and configure a control in a visual GUI builder is not automatic. Often, the developer must explicitly write considerable additional code (and do some other types of work too) in order to make this possible. This perhaps isn’t so surprising when you consider that your visual GUI tool has to know which aspects of the control are configurable, and what options to display (in the Inspector palette or such) to allow this. Each control is different, and so we have to be quite explicit about what the visual builder can and can’t do when setting up one of our new controls. This all requires time and effort, even though it may seem counterintuitive when you already have a control displaying on the screen and functioning within your application.
The message here for clients is that it’s best to be very specific about what you mean when using terms like “reusable” or “configurable”. Sometimes a term will be well-defined, and sometimes (often) it won’t - and people don’t always even agree on what “well-defined” means. Be explicit, and avoid assumptions at all costs. We developers would much rather have things spelled out in what may seem like ridiculous, patronising detail, because it helps us make sure that we understand what you want - and that’s the first step in actually delivering it to you.
Can it talk to the server?
This is an extremely common request, and it masks a multitude of possible situations and potential pitfalls. I’ve often referred to the “hidden server problem”, where the fundamental assumptions of what’s actually involved in online functionality vary so much between clients and developers that whole projects can be derailed.
It’s reasonable to think this is easy.
The expectation that online functionality is easy can be separated into two levels: all online functionality is easy, and this specific online functionality is easy. Let’s talk about those one at a time.
A huge percentage of apps have online functionality these days. Some of them have sophisticated integration with remote websites or other networked computers, and some just have blink-and-you’ll-miss-it online features like checking to see if a newer version of the app is available to download. It’s getting to the point that there are maybe more apps with some basic online component than those without, and we’re all also well aware of the number of web start-up companies and services around.
Everyone and everything is online, and ubiquity has a strong correlation with comprehensibility. Things that aren’t readily comprehensible don’t become ubiquitous, and things which are comprehensible tend to spread and be investigated or adopted by others, leading to wider understanding and a reduced perception of difficulty. The internet is both a primary mechanism for this process, and also a significant example of it at work - the task of making desktop software communicate with a remote server is extremely well-understood, and is indeed made almost trivial in many application frameworks and/or programming languages. Doing “online stuff” in general is seen as easy, and that’s a powerful guide for client expectations.
Now, we have the specific case of this actual app’s online functionality. It’s probably not a massively complex thing (using the reasoning that the vast majority of examples of anything are not the most complex examples; that’s just part of the bell-curve nature of the universe), and so the client will reasonably expect that achieving the functionality online isn’t too complex either. It’s just storing the user’s login and password on the company’s webserver, or it’s just pulling down an up-to-date list of recent messages from a server, or some other such thing. True; they do sound simple.
The reality is more complex.
The actual reality of the effort required to add some kind of server integration depends on the client’s understanding of what the server-side stuff actually is. I’ve found that clients tend to fall into two broad categories in this regard: (1) they know pretty much everything there is to know about the server side, and indeed maybe it’s their main business; or (2) they have no idea what’s involved on the server side, and implicitly assume it’s pretty much the work of but moments to set something up remotely and exchange data with it.
The first group of clients bear little discussion (but much gratitude). They’re very likely well-prepared for the work involved, and have perhaps even already decided what server-side technologies they’ll be using (such as a scripting language or such), what database server they’ll use, and maybe even the actual structure and format of the data itself. They might even already have rolled their own web services, which are even now patiently waiting for the app’s nascent connections. Chalk that one up in the “win” column, and continue work as normal.
The second group of clients have a large surprise coming - and that’s fine; they should no more be expected to know the ins and outs of server-side software or configuration than they should for the desktop equivalent. The surprise is that, for all the ubiquity of the internet and web sites and services, it’s not actually fundamentally any easier than writing stuff for the desktop.
The problem is that you don’t necessarily get anything for free on the server side, unless your needs are very simple and general. It’s possible that you’ll not only have to set up the software that listens for connections from your app, but to also write some code to deal with those connections and possibly also talk to a database to store or retrieve data for the desktop app. Even the very format of that data will have to be decided in advance, explicitly, and configured on the server. For anything but simple cases, it can be like a whole new project. This is doubly true if there’s to be an actual website which your users can visit and interact with - that’s literally an entirely new project, and maybe an even longer and costlier one than the desktop app.
If you’re a client and you know that you want some online functionality, you need to discuss it with your developer right at the start of the project - online functionality is the mother of all “don’t leave it til the last minute” aspects of a project (well, maybe interaction and interface design is the mother, but your online functionality is at least the intimidating, authoritarian nanny who rules the roost til mother gets home after work). Talk about it immediately. Even if you’re certain that you know what’s involved and have it covered, your developer still needs to know. You’ll save yourself a whole world of pain later.
This is a shared responsibility.
Let’s try to sum up. I’ve tended to conclude each of the requests above with advice for the client, but making the development process a smooth and mutually respectful one is very much a shared responsibility. It’s very important to realise that both parties (client and developer) have a critical role to play.
The client is paying to make their project a reality, and thus has a responsibility (to themselves, more than anyone else) to have a clear vision of the intended functionality of the software and scope of the project. They must also be willing to consider and discuss the areas they’re not sure of - and be willing to live with the inevitable consequences of failure to do so.
The developer has a professional duty to realise that the client will often (and quite reasonably) not fully understand that they want, and may certainly not comprehend the full ramifications of all the functionality they request. It’s an inseparable part of the process of requirements gathering to ensure not only that you understand the client’s spec, but that the client does too. It’s due diligence, and it’s a sure way to reduce your own pain later.
A developer cannot exist in a professional clean-room; he or she must be a teacher and a translator and a negotiator too. If you can’t do that, then you shouldn’t be dealing directly with clients, and/or you should perhaps have a pointed conversation with whomever does talk to your clients for you.
A key realisation is that every point addressed towards the “client” can equally be directed towards the developer’s manager, and this is about ten times more relevant if that manager (inexplicably, in my opinion) has no development experience of their own. If you’re in such a position, remember that things you don’t understand are not necessarily easy, and your own job performance is likely contingent on your expectations and perceptions being comparable to those of your developers, moreso than your clients.
If there’s one single lesson in all this, it’s that in development projects (and almost certainly in all endeavours of any kind, throughout all of life) communication is usually good and assumptions can be very bad. This shouldn’t be news to anyone, but sometimes we must remind ourselves to remain consciously aware of it.
There will be many more questions besides the few I discussed above. If you have one you’d like to add, please go right ahead by using the comments form below (it might be handy if you used the same basic format as I used above). Likewise, any general comments, statements of agreement or disagreement, angry tirades or such are of course always equally welcome. Thanks for reading.