What this Article is About
It is important for people involved in project decisions to be aware of the consequences their decisions have on the project's success and cost in terms of time and money.
For 20+ years of my software development experience and 10+ years of consulting, I participated as an architect or a developer in many projects - most of them successful, some failing, but every project (whether successful or not) involved good and bad decisions made by various people.
The purpose of this article is to lay a foundation for project success by advocating decisions that in my experience are useful, and avoiding wrong decisions.
There is always a "political component" to Software Development - this article is not about that. The purpose of this article is to provide recommendations that would maximize client satisfaction while minimizing spending.
Main Guidelines for Project Development
Here, I introduce two main guidelines for software development, which will be described in more detail further in the article.
- There are many books, articles and even more advertisement junk written on the software development subject. Take it all with a grain of salt! If what you read conflicts with common sense - choose common sense.
- At company, project, or at the individual level, the propagation of requirements (especially the API requirements) should flow from the one who uses it (the client) to the one who builds it, not vice versa. By client, I also mean the developers within the same project who use software built by someone else. This very important rule is often ignored to the detriment of project success.
At the Project's Start
There are some important decisions to be made at the very start of a project. Theoretically, wrong decisions made at the start of the project can be corrected later. However, the more money or time are wasted on bad decisions, the less likely they will be corrected later, since correcting them will raise competence questions.
Choosing a Language
For a language, I strongly recommend against C++ (unless you are building some very high performance algorithms where split of a second is crucial) primarily due to its slow compilation time. Every software development (if done properly) involves a lot of prototyping and restarting the application or running tests for the application. Each new run after a code change involves a compilation. The C++ templates are built in such a way that code generation is a must at the compilation time so that C++ is usually very slow to compile. Slow compilation will result in slow prototyping and correspondingly slow development. Without templates, C++ is not as powerful as a modern strongly typed language should be.
Moreover, Java and C# have many standard libraries built into the language while C++ relies on third party libraries.
Optimal Team Size
In my experience smaller teams produce better software faster. E.g. for a desktop 3 tier project I would recommend a 3 people team - one person for every tier + an architect + 1-3 people for redundancy.
My most successful projects had precisely such teams of 3-7 members. One of the least successful projects I was a part of, had a team of 20+ members and it was not able to achieve in several years what smaller projects would achieve within months.
To Scrum or not to Scrum
Without a doubt - to Scrum! Agile/Scrum may not be perfect, but it is better than the alternatives. There are aspects of Scrum, however, that are not effective (I do not think they should be part of the process, even though most Agile textbooks would mention them). Below is a list of positive and negative features of scrum. My advice: avoid using the negative features listed below.
Positives of Scrum
- Frequently, at the beginning of a project, the clients themselves may not have a clear vision of what they need. It might take many iterations (including mistakes by clients and developers) for the clients to figure out what needs to be built and for developers to figure out how to build it. Scrum's iterative approach fits this paradigm best.
- Under scrum, the developers can estimate themselves how long a certain feature would take to implement - they can usually do it much better than the managers.
- Daily updates benefit both managers and developers. Both get a better picture of where the project is. Furthermore, for the developers, it is an opportunity to share with others what they have been working on, to learn what others are up to, and in general, it is good to exercise the non-programming parts of the brain while letting the programming parts rest a bit.
- End of Scrum meetings, including demos and planning, are also of benefit when conducted well, for the same reason as the daily meetings but on a larger scale.
Negatives of Scrum
- Some versions of scrum advocate tracking only performance of a team as a whole - disregarding individual developers' contributions. While team performance is of great importance, it is still important to recognize individual efforts.
- There are meetings at the end of the scrum where people beat their chests saying what went wrong - the more the better. I never saw anything good coming out of them. When there is an issue, it should be resolved between the developers. More serious issue should be escalated to the architect/manager and sometimes, changes in project procedures should be undertaken.
Never saw anything good coming out of peer programming unless it is short term and on a strictly voluntary basis by two willing partners with the purpose of sharing knowledge.
I practice very aggressive, fast, result driven programming - watching someone else program makes me want to sleep. Everyone I know who was ever forced to do peer programming shares my view on it.
Choosing Packages and 3rd Party Components
You should be very careful choosing 3rd party libraries - you should be skeptical of slick demos and advertisements. Choose a 3rd party package (either open source or something you need to pay for) only if one of the following conditions is
- It is a well known product - like MS Visual Studio, Oracle DB, MS SQL Server, Telerik or DevExpress controls - with THOUSANDS of testimonies
- Your developers, whom you trust and who are going to be working directly with this software, have experimented with this software for a reasonable time, at least for two weeks, and are comfortable using it.
Remember that the more money you pay for a software package - the more difficult it will be to replace or sideline it later because the people whose money you spent, will want to make sure that the money was not wasted.
Remember that great companies can still have lousy products. One team I was on many years ago, had a negative experience with Oracle's Stellent software - and they had spent about a quarter million dollars on it! To Oracle's credit, they just bought Stellent at the time - so it had been developed completely outside of Oracle.
Also, remember that spending a month researching 3rd party components in the beginning can save you literally years later.
Some Concrete Recommendations for 3rd Party Components
This section is leaned towards the MS Desktop universe.
Packages of UI Controls
For WPF front end, I recommend the following packages:
- Telerik - was built with WPF concepts in mind and easy to modify and learn.
- DevExpress - less WPF oriented (in my opinion) so it is less easy to customize, but still flexible and quite fast. It has some controls that Telerik does not have, including the designer control that allows users to assemble a WPF view at run time and geographical map control.
I do not know much about Xceed but from what I heard, it is also quite ok to use.
I would caution against Syncfusion - at least several years ago, it was built contrary to WPF ideology.
I would recommend against Infragistics - it has most out of the box functionality but, in my experience, it is difficult to customize, and in WPF, customization is the name of the game!
All of the above WPF packages have performance problems when it comes to charting. The WPF Chart package I worked with and strongly recommend is SciChart.
Whichever packages you choose to acquire - choose the license that includes the rights to download the source code. Whenever I used Telerik or DevExpress, I had to research their code in order to be able to produce the effects desired by my clients.
Most decent size projects should involve inversion of control in order to build extension points for the project or to facilitate swapping real implementation for testing implementation (e.g. when it comes to testing front end functionality against the back-end).
Most often, I used MEF2 which is a bit complex, but was always adequate for the project's purpose.
I heard many positive reviews of Ninject (though I never used it myself).
I am developing Roxy IoC container. It is still work in progress, especially when it comes to IoC, so I cannot recommend it now. Hopefully, I'll be able to recommend it in a few weeks.
Out and away, Xunit is the best testing framework I know and I highly recommend it. It is free. It allows to run the same tests with various attribute based inputs. In other frameworks, you would have to write separate code for each one of the input combinations.
In MS universe, I would recommend building a custom middle tier using either MVC Controller or WebApi or WCF. I also like using ADO Entity Framework at the middle tier level.
WCF is more complex and more powerful than ASP middle tier solutions. Often, ASP middle tier is quite sufficient for the purpose, but for more complex requirements, like using TCP connection instead of HTTP, WCF is great.
For a decent sized project, I recommend using either Oracle or MS SQL Server. In my experience, No SQL databases are used at most as a caching mechanism together with SQL databases or for some special operations e.g. global search.
I worked with many tools and here are the ones I recommend:
For source control, I recommend Git and Subversion; both work very well and both are free - my preference is for Git.
As a requirements/bug tracking system, I recommend Atlassian Jira or Rally. Both are highly configurable, well tested, solutions.
For build, release and continuous integration, I recommend Atlassian Bamboo.
Note that Atlassian software is very cheap when the number of users is less than 10. For such teams, each product has a flat rate of $10 per month.
Progressives vs Conservatives
There are always people on the team who are eager and willing to learn new software and new packages (I am one of those) and those who want to stick to old software and old packages that they know well and which worked for them before.
Here are some examples when these approaches were justified.
When Progressives were Right
- Long ago, switching from C++ to Java greatly increased the productivity on a project due to shortened compilation time and native packages.
- Switching to WPF and C# was definitely a great decision - WPF is the best Windows based desktop development package and C# now is more powerful than Java and is constantly improving.
- People are slow to adopt Reactive Extensions but it is definitely a great package which enables building many things that used to be too complex to build.
- Roslyn is a great C# compiler as a service and I believe it will eventually make people capable of implementing their own language features while providing full IDE support.
When Conservatives were Right
Over the past 10 years, Microsoft released many packages that failed. (This taught me caution).
- I am still very bitter about Silverlight. I think it was a great package for web development. I bet on Silverlight (even though I understood that it was a kind of philanthropy on the part of Microsoft since it was undermining its own platform) and I lost.
- Windows RT, multiple Windows Phone platforms, once actively promoted by MS failed. (I was already wiser and I did not feel good about them from the beginning)
Some Ideas on Predicting when a Package is going to Fail or to Succeed
I am afraid it is virtually impossible to predict with 100% certainty.
WPF is a great package and I fell in love with its concepts from the first glance (I had had many similar ideas before).
Silverlight was also a great package wholly maintained by MS, and when MS decided they do not want it anymore it basically died.
Windows RT was a flop and I felt like it was going to be a flop from the very beginning because they steered away from .NET and tried going into native.
I guess the rules of thumb are:
- When MS releases a great product and puts some muscle behind it - the product thrives.
- When MS releases a great product and discontinues it - the product dies.
- Even MS muscle cannot save a lousy product.
I would like to cap this subsection with a couple of predictions concerning the new MS packages.
Based on the limited information I have, I believe that Xamarin has a glorious future - it will hopefully become one of the chief means of programming smart phones and tablets.
I am afraid UWP is not going to fly, since it only works on Windows 10 and Windows 10 products. You won't be able to run it on Windows 8 or Windows 7, let alone Linux or Mac. Why use it if it is just a pale shadow of WPF and WPF runs on any Windows platform?
I was wrong before, I may be wrong again - time will show.
Prototyping Before or in the Beginning of the Project
Despite the iterative nature of Agile and the fact that clients may only have a vague notion of what the final product should be in the beginning, it is good practice for the project architect to come up with at least a minor prototype in the beginning of the project.
Such prototype should be end to end with some UI, middle tier and Database functionality as well as the rudimentary test projects. All parts of the application should be operable and communicating with each other and the tests should run.
Based on my experience, I would recommend spending 2 weeks to a month on the prototype. The architect should do it by himself with help from others only if requested by the architect.
The rest of the project will involve expanding the original prototype.
In addition to being an initial point of reference, such prototype will give the managers and the team confidence that the decisions they made and third party components they use will actually achieve the end goal.
3 Tier Architecture
Here is the standard 3 tier architecture schema:
On the diagram - the two sided black arrows mean request-response connection from the UI Clients to the middle tier and from the middle tier to the databases.
The one sided red arrows mean the 'hot' (real time) connections from live feeds to the middle tier and the clients (who are subscribed to it). Also, it shows that once the real time data is received, it may be recorded into the database (red arrows from the middle tier to the database).
There can be caches in the middle tier and individual client caches for speed. Usually, you cache data that you know will not change within a certain time period. When the time period passes, you force the data out of the cache or mark it 'stale' so that the data will be refreshed after the next request.
Important Note: In my experience, the best middle tier is one that does not have any business logic in it. The business logic should reside in the database and the UI. Middle tier should be a highly configurable generic gateway into the database base functionality and should not have to be modified every time the business logic is expanded. I built such middle tier several times in my life both in C# and Java.
Perhaps the second best middle tier is one which requires only minor and standardized modifications when new business logic is added. I worked with such middle tiers several times as well.
Running the Project
Client - Server API Requirements Propagation Principle
The main principle of multi-tier development is that client should have more say about the API that he uses than the one who implements it (the API should be client driven).
Just as the client (or the client proxy e.g. the product manager) of the software has more say about how the final product should look, so, too, the UI developer should have greater say about the API that he uses from the middle tier and the back-end.
I want to be clear about 2 points:
- I am not saying that the UI developer fully determines the API. The API should be nailed down in discussions between the one who requests it and the one who implements it - some things might not even be possible or very hard to achieve.
- The implementation of the server is still up to the server developer - I am only talking about the public API that the client needs.
The relationship between the UI developer and the back-end/middle tier developers should be exactly the same as that between the end user and the UI developer.
This principle is often not followed. In fact, people start developing from the back-end and the middle tier and then the UI has to paper around all the resulting API problems.
Imagine the UI developer coming to the end users and telling them that what he built is good for their needs even though he built it without any consultation with them. Sounds pretty ridiculous, yet the UI developers have to deal with similar situations regularly when the back-end developers tell them that now they have all they need even though they built it without any consultation with the UI developers.
My experience confirms this principle time and again - the requirements, APIs and even development should be propagated from the part that consumes the API to the part of implements it. This is true not only for the Client-Server division, but also when you yourself program a significant piece of software. You need to start with what the purpose of the software is and then fill in the 'blanks'. Otherwise, if you start with the 'blanks', you may find that they do not quite fit the purpose.
The popularity of the test driven development is another confirmation of this paradigm.
In the old 'Waterfall' philosophy, architects were trying to create many interfaces in the very beginning of the project so that the developers would program to them. This is not the best approach, from my point of view - it puts the cart before the horse.
Very few interfaces should be created at the onset and the architect should always be ready to modify them if it is found that they do not fit the purpose.
Interfaces should be created whenever there is a need for them - e.g. whenever there is a method or class that needs to deal with objects that satisfy a certain interface but whose implementations may vary. Do not try to too hard to foresee that you'll need an interface - first (when only one implementation is needed) program to a class, but be ready to change it to an interface when such need arises.
This is true on the individual level and on the level of an architect. The iterative nature of Agile is capable of accommodating API modifications.
The Role of an Architect
This brings me to the role of a software architect.
The role of an architect (especially when a project is of reasonable size) can be undertaken by one of the developers on the team.
Here are the tasks that the software architect should undertake:
- Overviewing the release process - moving the product to the users or user proxies.
- Keeping tab on the progress of the product development and changes in product requirements.
- In the very beginning - building a very sparse skeleton (prototype) of the product as mentioned above (including the initial testing projects).
- Resolving software issues between various developers.
- Figuring out the code commonalities and factoring them out into a common API that can be used in various places by various developers.
The last point should also be practiced by individual developers for their own code or if a developer sees a commonality in code across multiple developer domains, he should bring it to common consideration and let the architect decide if the commonality needs to be factored out.
In general, there should be constant refactoring of commonalities in the code, both by the individual developers and by the architect.
Some people think that they need to find and factor out all the commonalities in advance. This is not likely. No one can do it, just as no one can create all interfaces in the very beginning of a project. There is no reason to spend a lot of time thinking about the commonalities at the individual or architect level in the beginning. Throughout the code development, you'll be able to find which code to refactor.
My advice to the architect is to think in terms of the data shape throughout the multi-tier architecture. In relational database - the data is usually placed in normalized tables. There are also some records that may come via real time feeds. There is no hierarchy at that level. On the UI side, though, most of the records will need some hierarchy for displaying them to the users in the shape the user wants. I plan to write another article dealing specifically with data shape and data shape transformations.
Building a Team
In my experience, non-work related activities outside of the workplace are a great way to build trust among people and make people feel like they are a team. Here are some activities that work:
- Scavenger hunt
- Going out to a restaurant or for a drink
- Soccer games
Here are some people related issues that I observed throughout the projects
- Good leadership and political skills are not sufficient to run a software project. In fact, I saw projects fail because they were led by people with good leadership and political skill but without any sense of software development or with some sense of software development but were afraid to use it to argue against their superiors.
- People who speak with confidence at a meeting are not necessarily aware of software details. Important decisions should never be made at a single meeting - only after thorough written discussions.
I observed a so called 'quackery' problem when people who have no clue are trying to take over a certain area of coding or project leadership. They may have good leadership and oratorical skills and some of them may even have technical skills but in a different area. Such people should not be allowed to make decisions in areas they have no idea about.
One way to check if a person is an expert in an area, is to ask him to build a proof of concept prototype. If he cannot build a small proof of concept, he should not be allowed to take over the project.
I once worked with a person who stated that building a prototype would take too much time and either everyone just follows his lead or the whole project is going to collapse. This 'all or nothing' approach is a sure sign of quackery - it should always be possible to build a small proof of concept. The person had to leave the project and the project succeeded without him.
I conducted many interviews in my professional life and here are some points I would like to share:
- Be to the point - if you want to hire an Angular expert do not ask him many questions on WPF or on SQL.
- Test concepts, not nitti gritti details, for example, it is always good to test the person's understanding of relational database concepts but (unless you specifically hire an SQL expert), you do not have to test his knowledge of temporary tables in MS SQL Server.
- Sometimes people with good memory can learn a lot of information by heart and then use it for the interview. This is why it is important to have people actually come in and do a simple coding exam. This way can always see if the person has a hands on knowledge or he simply crammed everything a couple of days before.
Writing, Executing and Testing Requirements
The requirement should be nailed down between the developer and the one who orders the requirement - it may be an end user or an architect or another developer on the team. Since the developer's butt is on the line, the developer should always carefully read the requirement, making sure that no stone is left unturned before working on it.
Requirements (or Jiras) should usually be written with a single feature in mind. The feature may be a user feature or may be a feature that the end user will never see e.g. some refactoring; either way, most of the time, one requirement should have a single feature.
Once a requirement is created, new feature requests should not be added to it. It should be used as a road map from the requester to the QA. If it turned out that the developer implemented the requirement but the requirement was wrong or not complete - it is the requester's fault and a new Jira should be opened to cover the leftover. If, however, the requirement was not fulfilled by the developer - this is the developer's fault and the requirement should be re-opened and reassigned to the same developer for completion.
When it comes to UI development, there are also some small, easy to implement requirements (I call them 'UI nits') which can all go to a single Jira even though they can touch completely different features. You can open special 'nit' Jiras for them where 'single feature' condition is not necessary.
In this article, I give recommendations for starting and executing a multi-tier project based on my long experience as an architect and a developer.
I believe that following these recommendations can greatly minimize project cost in terms of time and money, and increase the value of the resulting product.
I would like to thank my dear wife, Pazit for reviewing and editing this article.