0% found this document useful (0 votes)
10 views

Design and Code Review Checklist: by Tim Star

Uploaded by

Richard Whipp
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views

Design and Code Review Checklist: by Tim Star

Uploaded by

Richard Whipp
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 47

Design and Code Review Checklist

By Tim Star
Table of Contents

VISUAL CHECKLIST
Intertech provides a easy to read checklist in the form of an infographic to make
sure your design and code are good to go. See the infographic

COMPREHENSIVE CHECKLIST ……………………………………………………………….…2


Is the class / procedure/ variable scope correct? …………………………………..………....4
Look at the code ……………………………..……………………………..………………..…………..…..6
Review for Don’t Repeat Yourself (DRY) Principle …………………….………..………………7
Review for Liskov Substitution Principle (LSP) …………………………………...……..……..11
Review for Open-Closed Principle (OCP) …………………………………………..……………..17
Review for Interface Segregation Principle (ISP) ………………………………….…………..24
Review for Single Responsibility Principle (SRP) ……………………………….….…………..29
Review for Dependency Inversion Principle (DIP) ……………………………………..……..32
Delegation vs. Inheritance ……………………………..……………………………………..…………39
Composition vs. Inheritance ……………………………..………………………..……..….………..41
YAGNI - You Ain’t Gonna Need It! ……………………………..………………………….………..44

INTERTECH CONTACT INFORMATION ……………………………………………………46

Page 1
www.Intertech.com

This Intertech checklist provides a comprehensive compilation of design and code review
principles for consideration during projects. There are items on the checklist that are outlined in
detail further on in the document and a few where we’ve provided links from this document to
quality design and review resources.

Blue Underlined Text Links to Further Detail


 Review Unit Tests
 Is the code in the right place?
 Does the name space make sense?
 Is the class / procedure / variable scope correct?
 Are the Classes, methods, and variables named correctly?
 Are the methods, and variables typed correctly?
 Look at the code.
 Review for Don't Repeat Yourself Principle (DRY) (Abstract common things and put in
single place).
 Review for Liskov Substitution Principle (LSP) (Subtypes must be substitutable for their
base types).
 Review for Open Closed Principle (OCP) (Open for extension closed for modification)
 Review for Interface Segregation Principle (ISP) (Clients should not be forced to depend
on methods that they do not use, Favor many smaller over one large interface)
 Review for Single Responsibility Principle (SRP) (Every object has a single responsibility.
All the object's services should be focused on that responsibility).
 Review for Dependency Inversion Principle (DIP) (High level modules should not depend
upon low-level modules. Both should depend upon abstractions.)
 Consider Delegation over Inheritance. If you don't need to change base class behavior,
consider delegating (handing over responsibility of a task) rather than inheritance.
 Consider Composition over Inheritance. Similar to delegation except the owner class
uses a set of behaviors and chooses which one to use at runtime. When the delegating
class is destroyed, so are all the child classes.

Page 2
www.Intertech.com

 Aggregation. Similar to composition except when the delegating class is destroyed, the
child classes are not.
 Consider Polymorphism. Make a group of heterogeneous classes look homogeneous
 Consider generics.
 Testability considerations?
 YAGNI (You ain’t gonna need it) When in doubt, leave it out!
 Does object wake up in a known good state (constructor)
 Consider Security

Page 3
www.Intertech.com

Is the class / procedure / variable scope correct?


Below is a scoping/access modifier cheat sheet:
Classes:
• Public Class - Access is not restricted.
• Private Class - Only valid for nested classes
• Internal Class - only visible to the assembly
Members:
• Private Member - only available to the containing type
• Protected Member - Available to the containing type and anything derived from the
containing type.
• Internal Member - available to current assembly.
• Protected Internal Member - Available to current assembly or anything derived from
containing type.
• Public Member - Access is not restricted
Valid Member Access Modifiers:
• Enum - public
• Interface - public
• Class - public, private, internal, protected, protected internal
• Struct - public, private, internal

Page 4
www.Intertech.com

Inheritance:
• Abstract class - cannot be instantiated. May have abstract and non abstract
methods. Derived class must implement all abstract methods.
• Sealed Class - Cannot be inherited
• Virtual Method - may be overridden
• Abstract method - no implementation, must be overridden
• Sealed method - Cannot be overridden
• Sealed Override Method - No longer may be overridden. Public sealed override
MyMeth()

Page 5
www.Intertech.com

Look at the Code


 Is the code logically correct?
 Are we following best practices?
 Are we considering security?

Did we run a code review? If you have a team or enterprise developer edition, the code review
may be found in the Analyze menu.

 Is the code maintainable?


 Is our code unnecessarily complex? I always favor simplicity until forced to do
otherwise.
 Is the error handling effective?
 Will our code perform? I tend to assume it will until proven otherwise.
 Are our objects loosely coupled?

Page 6
www.Intertech.com

Don’t Repeat Yourself Principle


DRY in the DRY principle is an acronym for Don’t Repeat Yourself. While we are going to focus
on applying the principle to code, it can and should be applied to more than just code. The can
be applied to database schemas, tests, documentation, test plans, design documents, build
scripts and more. Any time we start duplicating our work we are signing up for parallel
maintenance and chances are we will eventually have the items fall out of synch.
We can use several techniques to avoid duplicate code. Some obvious things we can do include
using classes and methods to organize common code. We can write an abstract or base class to
implement common behaviors for multiple derived classes.
We can use properties to perform a validation in one place rather than performing the
validation anywhere we want to use a member variable:

1. [System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.
Never)]
2. int aComplexInteger;
3. public int AComplexInteger
4. {
5. get { return aComplexInteger; }
6. set
7. {
8. if (value == 0)
9. throw new ArgumentOutOfRangeException("AComplexInteger");
10. if (value != aComplexInteger)
11. {
12. aComplexInteger = value;
13. //Maybe raise a value changed event
14. }
15. }
16. \

One of my favorite techniques is constructor chaining. If we have multiple constructors that


perform similar logic, we should use constructor chaining to avoid duplicating code:

1. using System;
2. using System.Collections.Generic;
3. using System.Linq;
4. using System.Text;
5.
6. namespace CicMaster
7. {
8. class BetterConstructorLogic
9. {
10. #region WhyItIsBetter
11. //No duplicate code, this is called constructor chaining

Page 7
www.Intertech.com

12. //step through this


13. #endregion
14. string someString = string.Empty;
15. int someInteger = 0;
16. List<int> myIntegers = new List<int>();
17.
18. public BetterConstructorLogic():this("A Default Value")
19. {
20. //someString = "A Default Value";
21. System.Diagnostics.Debug.WriteLine("In Default Constructor");
22. }
23.
24. public BetterConstructorLogic(string aString):this(aString,123)
25. {
26. //someInteger = 123;
27. System.Diagnostics.Debug.WriteLine("In one param constructor");
28. }
29.
30. public BetterConstructorLogic(string aString, int anInteger)
31. {
32. System.Diagnostics.Debug.WriteLine("In two param constructor");
33.
34. someString = aString;
35. someInteger = anInteger;
36. myIntegers.Add(someInteger);
37. myIntegers.Add(someInteger ^ 3);
38. myIntegers.Add((int)(2 * 3.14 * someInteger));
39. }
40. }
41. }

The final technique I would like to mention is use a factory to create all but the simplest objects.
The following (admittedly nonsensical) code needs to execute maybe a half dozen lines of code
to construct an Order object.

1. using System;
2. using System.Collections.Generic;
3. using System.Linq;
4. using System.Text;
5. using System.Threading;
6.
7. namespace BusinessLayer
8. {
9. class ObjectContext
10. {
11. public ObjectContext(String username)
12. {
13. //Look up the user permissions
14. }
15. public bool IsInTranaction { get; set; }

Page 8
www.Intertech.com

16. public bool BeginTransaction()


17. {
18. //do some transaction logic
19. return true;
20. }
21. }
22. class Order
23. {
24. public Order(ObjectContext theContext)
25. {
26.
27. }
28.
29. }
30. class Consumer
31. {
32. public Consumer()
33. {
34. ObjectContext ctx = new ObjectContext(Thread.CurrentPrincipal.Identi
ty.Name);
35. if (!ctx.IsInTranaction)
36. {
37. if (ctx.BeginTransaction())
38. {
39. //...
40. }
41. }
42. Order order = new Order(ctx);
43.
44. }
45. }
46.
47. }

Duplicating these few lines of code in a couple places is not that difficult. Now say the
application is enhanced and grows for a few years and suddenly we see this code duplicated
dozens or hundreds of times. At some point it is likely that we want to change the construction
logic, finding and changing all the code we use to create the order is difficult, time consuming,
and a QA burden.
A better approach would be to encapsulate the logic required to build a new order. Here is an
implementation using a simple factory. It is much easier to find, change, and test this code:

1. static class OrderFactory


2. {
3. public static Order GetOrder()
4. {
5. ObjectContext ctx = new ObjectContext(Thread.CurrentPrincipal.Id
entity.Name);

Page 9
www.Intertech.com

6. if (!ctx.IsInTranaction)
7. {
8. if (ctx.BeginTransaction())
9. {
10. //...
11. }
12. }
13. return new Order(ctx);
14. }
15. }
16. class DryConsumer
17. {
18. public DryConsumer()
19. {
20. Order order = OrderFactory.GetOrder();
21. }
22. \

If we recognize that we are duplicating even a few lines of code over and over we need to take a
serious look at the code and figure out a way to encapsulate the logic.

Page 10
www.Intertech.com

Liskov Substitution Principle


According to Wikipedia, the Liskov Substitution Principle states that “Objects in a program
should be replaceable with instances of their subtypes without altering the correctness of that
program”. This means that structurally our derivations should not alter the shape of the base
type in a way that could break the correctness of consumers, and behaviorally subtypes must
not violate reasonable assumptions about the base type. The rule is meant to help guide us in
how we use inheritance when we design our object hierarchies. Ten years ago I thought about
inheritance primarily as a method to reuse code. Code reuse is certainly one of the benefits of
inheritance but when I think back to some of the hierarchies I designed without considering LSP
I would suggest that using inheritance at all was more trouble than it was worth. The idea with
Liskov Substitution Principle is that if we follow this principle, consumers of the hierarchy will
remain working even when faced with using a new derivation. The principle has structural
requirements and behavioral requirements that must be followed to apply the principle
correctly. As with most things, the easiest way for me to wrap my head around a concept is via
examples. Let’s tackle the structural requirements first and look at example violations.
Consider the following base class:

1. using System.Collections.Generic;
2.
3. namespace LSP
4. {
5. public abstract class WidgetBase
6. {
7. public virtual IList<int> SomeList(string title)
8. {
9. //Some useful boilerplate implementation
10. return new List<int>();
11. }
12. }
13. }

It is a simple abstract class with one method (for now) named SomeList that accepts a string
argument and returns a generic IList of type int. Messing with the signature would certainly be
a violation of the principle but the good news is the compiler saves us from that, changing the
parameter type or return type in our derivation will not compile.

1. using System.Collections.Generic;
2.
3. namespace LSP
4. {
5. public class BrokenWidget:WidgetBase
6. {
7. public override IList<int> SomeList(object title)

Page 11
www.Intertech.com

8. {
9. return new List<int>();
10. }
11.
12. public override IList<object> SomeList(string title)
13. {
14. return new List<object>();
15. }
16. }
17. }

If we are only considering code reuse we might be tempted to say something like “My derivation
is almost like a widget but I don’t have the notion of ‘SomeList’ so I just won’t implement it”.
This will compile:

1. using System;
2. using System.Collections.Generic;
3.
4. namespace LSP
5. {
6. public class AlmostAWidget:WidgetBase
7. {
8. public override IList<int> SomeList(string title)
9. {
10. throw new NotImplementedException();
11. }
12. }
13. }

Consider a consumer using an Inversion of Control container to resolve WidgetBase, it will be


perfectly happy getting any concrete widget that returns an IList<int> when it calls SomeList but
if it gets a hold of the AlmostAWidget class above, it will get an unexpected runtime error when
SomeList is called and probably appear to be broken. Throwing this unexpected exception could
be considered structural or behavioral, it doesn’t really matter as the result is the same. A poor
derivation broke working code.
Here is a subtle problem with C# array covariance. in C#, covariance for arrays enables implicit
conversion of an array of a more derived type to an array of a less derived type. But this
operation is not type safe meaning it will compile but can cause problems that are only
discovered at run time. As an example, we will add the following method to WidgetBase:

1. public virtual WidgetBase[] Relatives()


2. {
3. return new WidgetBase[0];
4. }

Page 12
www.Intertech.com

Now let’s add two more derivations to our Model, NotLspWidget which makes use of array
covariance:

1. namespace LSP
2. {
3. public class NotLspWidget:WidgetBase
4. {
5. public override WidgetBase[] Relatives()
6. {
7. return new NotLspWidget[1] { this };
8. }
9. }
10. }

And LspWidget which does not:

1. namespace LSP
2. {
3. public class LspWidget : WidgetBase
4. {
5. public override WidgetBase[] Relatives()
6. {
7. return new WidgetBase[1] { this };
8. }
9. }
10. }

Notice that with NotLspWidget the backing store for the Relatives array is an array of type
NotLspWidget (the derived type) but with LspWidget the store is an Array of type WidgetBase,
Now consider the following unit tests which will act as our consumer code (the code we, as good
designers, are trying not to break by applying the Liskov Substitution Principle rules correctly to
our object hierarchy).

1. using System;
2. using Microsoft.VisualStudio.TestTools.UnitTesting;
3. using LSP;
4.
5. namespace UnitTestProject1
6. {
7. [TestClass]
8. public class UnitTest1
9. {
10. [TestMethod]
11. public void TestLspCovariance()
12. {
13. WidgetBase w = new NotLspWidget();
14. WidgetBase[] relatives = w. Relatives();

Page 13
www.Intertech.com

15.
16. //try to put some other WidgetBase to the array
17. relatives[0] = new LspWidget();
18. Assert.IsTrue(true);
19. }
20. [TestMethod]
21. public void TestLspCovariance2()
22. {
23. WidgetBase w = new LspWidget();
24. WidgetBase[] relatives = w.Relatives();
25.
26. //try to put some other WidgetBase to the array
27. relatives[0] = new NotLspWidget();
28. Assert.IsTrue(true);
29. }
30. }
31. }

The tests are really simple and similar. In test one I get the relatives from the NotLspWidget,
then try to add and LspWdiget to the array but we get a runtime exception:

Test two does the exact reverse and works perfectly well because the designer of LspWidget did
not rely on covariance to “convert” the returned array. This sounds like a contrived example but
if our consumers are using abstractions, as they should be, they should be able to add abstract
types to arrays of what appear to be abstract types without worrying about this bit of .Net trivia.

Page 14
www.Intertech.com

Now let’s handle the more difficult application of the behavior portion of the principle. For this
we must consider the reasonable assumptions consumers of our object hierarchy will make.
Method Hiding is a pretty nasty violation of the Liskov Substitution Principle that can be
particularly difficult to chase down. For this example we will add the following method to
WidgetBase:

1. public virtual long DoMath(int number)


2. {
3. return number * number;
4. }

Now we hide this method in NotLspWidget by using the new key word and changing the
algorithm. Note, there is nothing wrong with changing the algorithm!

1. public new long DoMath(int number)


2. {
3. return number + number;
4. }

Now execute this simple test:

1. [TestMethod]
2. public void TestHiddenMethod()
3. {
4. NotLspWidget w = new NotLspWidget();
5. Assert.AreEqual(w.DoMath(3), ((WidgetBase)w).DoMath(3));
6. }

The reasonable assumption is that if I call DoMath on NotLspWidget and DoMath on


NotLspWidget upcasted to WidgetBase the answer is the same. Execute the unit test and you
will find that a different DoMethod is called depending if you called it via the concretion or a
reference upcasted to the base type.
The final example I want to discuss is purely one of convention. Let’s say our object hierarchy
contains a lot of generic lists and these lists are frequently null or empty. As the designer of the
hierarchy we may choose to make it a policy that all methods that return Lists<T> shall return an
empty list rather than a null. This behavior would be documented for our consumers. In fact I
do prefer this approach to lists as now it removes the burden of checking for null on all methods
that return a list from my consumers and makes for a much cleaner code base. What happens if
a derivation comes along and violates this policy by implementing the following (or any other
code that can result in a null being returned)?

1. public override System.Collections.Generic.IList<int> SomeList(string title)

Page 15
www.Intertech.com

2. {
3. return null;
4. }

All consumers of this code will likely break.


In summary what constitutes a Liskov Substitution Principle violation?
• Throwing Not Implemented Exceptions.
• Making use of Method Hiding (the New keyword on virtual methods)
• Making use of array covariance on public return types
• Violating the implied or documented behavior of a base class (returning null instead
of empty lists for example)

Page 16
www.Intertech.com

Open Closed Principle


The open closed principle states “software entities (classes, modules, functions, etc.)
should be open for extension, but closed for modification”. For the sake of this section I
will use the term “server” to describe a class in an API – a business object for example.
“Client” will be used to describe the consumer of a service – it could be a user interface
component for example. The client has a dependency on a server at some level, the
server knows nothing about a client.
When considering the open closed principle I think it is interesting to consider the
original definition. According to Wikipedia, Bertrand Meyer originated the principle in
1988 and I’ll paraphrase his definition as stating that once a class is completed it could
only be modified to fix errors. If you wanted to add any new features or change the
behavior in any way you had to create a new class. Inheritance may be used to reuse
code from the original class but the resulting interface of the new class need not be the
same as the interface of the original class. Consider client class A that depends on
server class B. Once A and B are working they would never change. Now we create
server C which is an extension of B (reuses implementation but may have a different
interface). We have extended the API by creating server C but we did not change A or B
so A continues to behave correctly. The focus here is on how we can extend our API but
keep clients working by not changing any of the working code.
According to Wikipedia, Robert Martin is credited with popularizing the more modern
application of the principle which advocates the use of abstract interfaces. With this
approach, server implementations can be changed, and multiple server
implementations can be created and substituted for each other in the client. Again
consider client class A that depends on server class B but this time B is an abstract class.
Now we create server class C which is a derivation of B. Since client A is dependent on
an abstraction, we can extend the client by passing it C without changing A. The focus
here is on opening the client to extension but closing the client to modification.
Meyer’s approach is better in that it shouldn’t be possible to break working client A by
adding server derivation C. The down side, however, is that our answer to all change is
to add more code, client A will not be able to make use of server extension C, and
refactoring of B is not allowed. With Martin’s approach we are essentially saying “as
long as we respect the interface, never mind what I do to the server”… a pretty bold
statement. The down side here is that it is easy to imagine breaking client A by adding a
poorly designed server derivation C or even by carelessly refactoring B and inadvertently
adding a bug. See the Liskov Substitution Principle for some examples of bad
derivations. The upside is that with this approach, client A may be extended to make
use of server C without changing the client and since we have the freedom to refactor
our servers we should have less code, we can make use of new language features where
it makes sense, and we can reorganize things as our API evolves. In short the resulting

Page 17
www.Intertech.com

code should be easier to maintain. If we do not have good unit tests in place I would
suggest that Meyer’s approach is the better way to go but if we do have a good suite of
unit tests in place I am going to favor Martin’s approach
We are not going to look at Meyer’s application of the principle as it is straight forward
and intuitive but let’s look at some violations of Martin’s application of the principle.
Consider the following code:

1. namespace Solid.OCP.Violation
2. {
3. public class DbService
4. {
5. public bool Create()
6. {
7. throw new NotImplementedException();
8. }
9.
10. public int Update()
11. {
12. throw new NotImplementedException();
13. }
14.
15. public List<string> Select()
16. {
17. throw new NotImplementedException();
18. }
19.
20. public int Delete()
21. {
22. throw new NotImplementedException();
23. }
24. }
25. }

Conceptually DbService is a part of an API responsible for persisting something to a database.


The method implementation is not important, only the shape. It is important to note that
DbService does not make use of any sort of abstraction.
Imagine now that after some time we decide we also want to use a NoSql structure to store
some of our data, we might create the following server:

1. namespace Solid.OCP.Violation
2. {
3. public class AzureTableService
4. {
5. //Assume Azure Table Storage is NOT updatable
6. //which is not true
7.

Page 18
www.Intertech.com

8. public bool Create()


9. {
10. throw new NotImplementedException();
11. }
12.
13. public List<string> Select()
14. {
15. throw new NotImplementedException();
16. }
17.
18. public int Delete()
19. {
20. throw new NotImplementedException();
21. }
22. }
23. }

Again, the method implementation is not important. The shape of the class is similar to
DbService but there is no use of abstraction.
If we need to create a client that works with both services it might look like this:

1. namespace Solid.OCP.Violation
2. {
3. public class Client
4. {
5.
6. public void Save(List<object> persitables)
7. {
8.
9. foreach (var item in persitables)
10. {
11. if(item is AzureTableService)
12. {
13. AzureSave((AzureTableService) item);
14. }
15. else if(item is DbService)
16. {
17. DbSave((DbService)item);
18. }
19. LogMessage("Somebody updated something");
20. }
21. }
22.
23. private void DbSave(DbService container)
24. {
25.
26. if(container.Select().Count >0 )
27. {
28. container.Update();

Page 19
www.Intertech.com

29. }
30. else
31. {
32. container.Create();
33. }
34.
35. }
36.
37. private void AzureSave(AzureTableService container)
38. {
39.
40. if (container.Select().Count > 0)
41. {
42. container.Delete();
43. }
44.
45. container.Create();
46.
47. }
48.
49. private void LogMessage(string msg)
50. {
51. //Log an audit record
52. }
53. }
54. }

Consider the Save method. Conceptually we want to save a list of “things”, if the object doesn’t
exist in the storage device we should create it otherwise we want to update it (this is called an
upsert). Since we didn’t use abstraction, the list of things must be of type “object”. Next we
iterate through the list and inspect each object then we have to run code specific to the type of
object to accomplish the save. There are many problems with this. First if an object other than
AzuerTableService or DbService is in the list we are going to blindly do nothing – the alternative
would be to throw an exception but neither behavior is good. Second, the client must
implement a specialized method for each type of server. Finally, if we want to add a new server
to the mix we have to crack open the client, modify the if/then or maybe add a switch as the
number of different Servers we support grows and we also have to add the specialized method
mentioned above for each Server.
Now consider a design where we make use of an abstraction. First we will define an abstraction
called IUpsertable

1. namespace Solid.OCP.Better
2. {
3. //************ Put this in a seperate assembly ************
4.
5.

Page 20
www.Intertech.com

6. //Create an abstraction for the services


7. public interface IUpsertable
8. {
9. bool Create();
10. List<String> Select();
11. int Upsert();
12.
13. }
14. }

We are using an interface, it could also have been an abstract class if there was some shared
implementation.
The key now is that each of our servers is going to implement this interface:

1. namespace Solid.OCP.Better
2. {
3. //************ Put this in a seperate assembly ************
4.
5. public class DbService:IUpsertable
6. {
7. public bool Create()
8. {
9. throw new NotImplementedException();
10. }
11.
12. public int Update()
13. {
14. throw new NotImplementedException();
15. }
16.
17. public List<string> Select()
18. {
19. throw new NotImplementedException();
20. }
21.
22. public int Delete()
23. {
24. throw new NotImplementedException();
25. }
26.
27.
28. public int Upsert()
29. {
30.
31. if (this.Select().Count() > 0)
32. {
33. return this.Update();
34. }
35. this.Create();

Page 21
www.Intertech.com

36. return 1;
37. }
38. }
39. }

1. namespace Solid.OCP.Better
2. {
3. //************ Put this in a seperate assembly ************
4.
5. public class AzureTableService : IUpsertable
6. {
7. public bool Create()
8. {
9. throw new NotImplementedException();
10. }
11.
12. public int Upsert()
13. {
14. int count = 1;
15. if (this.Select().Count()>0)
16. {
17. count = this.Delete();
18. }
19. this.Create();
20. return count;
21. }
22.
23. public List<string> Select()
24. {
25. throw new NotImplementedException();
26. }
27.
28. public int Delete()
29. {
30. throw new NotImplementedException();
31. }
32.
33. }

And here is how our new client may look:

1. namespace Solid.OCP.Better
2. {
3. public class Client
4. {
5. public void Save(List<IUpsertable> persitables)
6. {

Page 22
www.Intertech.com

7.
8. foreach (var item in persitables)
9. {
10. item.Upsert();
11. LogMessage("Somebody updated something");
12. }
13.
14.
15. }
16. private void LogMessage(string msg)
17. {
18. //Log an audit record
19. }
20.
21. }
22. }

We have made a couple key design decisions here. First we decided that the DbService and
AzureTableService should be responsible for implementing an Upsert method and thus removed
the burden of adding a server specific method in our client. Second, by having the Save method
use a parameter of type List<IUpsertable> there is no danger of getting any type of object into
the method that does not implement the IUpsertable interface. Finally, the Save method no
longer needs to know anything about the concrete types that are being passed in, it may simply
iterate the list and call Upsert on each object in the list.
By using an abstraction in our Save method we can add any number of servers to the system
and have the client use them without ever having to modify the Client. This discussion has been
limited to class design but we should mentally go through this exercise with each method as
well. In the Client above our Save method requires a List<IUpsertable> parameter to be passed.
In our implementation we are doing nothing to alter the contents of the list so an even better
design would be to define the save method as follows:

1. public void Save(IEnumerable<IUpsertable> persitables)

By defining the parameter as List<T> we limited the possible consumers of this client to
consumers that had a List of IUpsertable objects. Modifying the parameter to
IEnumerable<IUpsertable> opens up our potential clients to those that have a List, Array or any
other structure that Implements the IEnumerable interface.

Page 23
www.Intertech.com

Interface Segregation Principle


According to Wikipedia the interface segregation principle (ISP) states that no client should be
forced to depend on methods it does not use. The interface segregation principle was
formulated by Robert Martin in the mid 1990s. For the sake of this section I will use the term
“server” to describe a class in an API – a business object for example. “Client” will be used to
describe the consumer of a service – it could be a user interface component for example. The
client has a dependency on a server at some level, the server knows nothing about a client.
As an example let’s assume a team responsible for creating the server was tasked with creating
a basic email object. Being good programmers they designed to an interface and came up with
the following Interface and implementation.

1. public interface IMessage


2. {
3. IList<String> ToAddresses { get; set; }
4. string MessageBody { get; set; }
5. string Subject { get; set; }
6. bool Send();
7. }
8.
9. public class SmtpMessage:IMessage
10. {
11. public IList<String> ToAddresses { get; set; }
12. public string MessageBody { get; set; }
13. public string Subject { get; set; }
14.
15. public bool Send()
16. {
17. //Do the real work here
18. return true;
19. }
20. }

Client Team A, responsible for creating an application that sends an email reminder as a part of
their process, creates a client that consumes the server and everything works. Now assume the
server team gets a new requirement, from Client Team B, and now they need to support
sending a text message. That’s almost the same as sending an email message so they decide to
be polymorphic and use the IMessage interface. Their new class looks like this: (can you spot
the Liskov Substitution Principle violation?)

1. public class SmsMessage : IMessage


2. {
3. public IList<String> ToAddresses { get; set; }
4. public string MessageBody { get; set; }
5. public bool Send()

Page 24
www.Intertech.com

6. {
7. //Do the real work here
8. return true;
9. }
10. public string Subject
11. {
12. get
13. {
14. throw new NotImplementedException();
15. }
16. set
17. {
18. throw new NotImplementedException();
19. }
20. }
21. }

Client Team B is happy, things work great and Client Team A is happy because now they can
send SMS messages if they want to. Client Team A wants to enhance their system to allow for a
list of addresses to be included as a blind copy so they give the Server team this new
requirement. The server team comes up with the following change to the interface and new
implementations:

1. public interface IMessage


2. {
3. IList<String> ToAddresses { get; set; }
4. string MessageBody { get; set; }
5. string Subject { get; set; }
6. bool Send();
7. IList<String> BccAddresses { get; set; }
8. }
9.
10. public class SmtpMessage:IMessage
11. {
12. public IList<String> ToAddresses { get; set; }
13. public IList<String> BccAddresses { get; set; }
14. public string MessageBody { get; set; }
15. public string Subject { get; set; }
16.
17. public bool Send()
18. {
19. //Do the real work here
20. return true;
21. }
22. }
23.
24. public class SmsMessage : IMessage
25. {
26. public IList<String> ToAddresses { get; set; }

Page 25
www.Intertech.com

27. public string MessageBody { get; set; }


28. public bool Send()
29. {
30. //Do the real work here
31. return true;
32. }
33. public string Subject
34. {
35. get
36. {
37. throw new NotImplementedException();
38. }
39. set
40. {
41. throw new NotImplementedException();
42. }
43. }
44. public IList<string> BccAddreses
45. {
46. get
47. {
48. throw new NotImplementedException();
49. }
50. set
51. {
52. throw new NotImplementedException();
53. }
54. }
55. }

Client Team A gets the new server and things are great but Client Team B is now broken and
must recompile and go through a round of regression testing because a feature was added for
another team.
The main goal of the Interface Segregation Principle is to loosen the coupling between servers
and their clients. An SMS message supports a list of addresses, a message body, and the notion
of sending but Client B was unnecessarily also coupled to the Subject and eventually the
BccAddressList property. In short, a change to any unused property or method or a change to
an overly broad interface itself will cause Team B problems. Let’s rework things a little bit and
see if we can improve the design.

1. public interface IMessage


2. {
3. IList<String> ToAddresses { get; set; }
4. string MessageBody { get; set; }
5. bool Send();
6. }
7.

Page 26
www.Intertech.com

8. public interface IEmailMessage:IMessage


9. {
10. string Subject { get; set; }
11. IList<String> BccAddresses { get; set; }
12. }
13.
14. public class SmtpMessage : IEmailMessage
15. {
16. public IList<String> ToAddresses { get; set; }
17. public IList<String> BccAddresses { get; set; }
18. public string MessageBody { get; set; }
19. public string Subject { get; set; }
20. public bool Send()
21. {
22. //Do the real work here
23. return true;
24. }
25. }
26.
27. public class SmsMessage : IMessage
28. {
29. public IList<String> ToAddresses { get; set; }
30. public string MessageBody { get; set; }
31. public bool Send()
32. {
33. //Do the real work here
34. return true;
35. }
36. }

With this design we have a nice crisp IMessage definition that will serve Client B very well in that
the client does not depend on any methods it does not use. We have created a “wider”
interface Called IEmailMessage that inherits from IMessage for Team A to use so Team A can
send Email or an SMS text message.
If we want to remove the requirement that a Client understand it needs to set the address list
and message body before calling the send method we could also do this:

1. public interface IMessage


2. {
3. bool Send(IList<String> toAddresses, string messageBody);
4. }
5.
6. public interface IEmailMessage:IMessage
7. {
8. string Subject { get; set; }
9. IList<String> BccAddresses { get; set; }
10. }
11.

Page 27
www.Intertech.com

12. public class SmtpMessage : IEmailMessage


13. {
14. public IList<String> BccAddresses { get; set; }
15. public string Subject { get; set; }
16. public bool Send(IList<String> toAddresses, string messageBody)
17. {
18. //Do the real work here
19. return true;
20. }
21. }
22.
23. public class SmsMessage : IMessage
24. {
25. public bool Send(IList<String> toAddresses, string messageBody)
26. {
27. //Do the real work here
28. return true;
29. }
30. }

Think of an interface as reasons for a client to change. The larger the interface, the more likely
it is that you will need to change clients in the future. An interesting but positive side effect is
that the smaller interface also “lowers the bar” for new servers that wish to implement the
interface. When the next great message medium comes along the new server only needs to
implement a very small IMessage interface to work with clients already using other IMessage
servers. In summary, favor many client-specific interfaces over one larger general purpose
interface.

Page 28
www.Intertech.com

Single Responsibility Principle


According to Wikipedia the single responsibility principle states that every class should have
responsibility over a single part of the functionality provided by the software, and that
responsibility should be entirely encapsulated by the class. All its services should be narrowly
aligned with that responsibility. The principle is one of the SOLID design principles as defined by
Robert Martin in his book Agile Software Development: Principles, Patterns, and Practices.
An important thing to note is that Martin defines a responsibility as “a reason to change” so
when I think of the principle I paraphrase it as a class should have only one reason to change.
When I am designing a system and it is time to start creating classes I always ask myself what
things change together and what things change independently of one another. When I find
things that change together they can and often should be put together in the same class when
they vary independently, I make sure and separate those things by using different classes.
Let’s revisit my Interface Segregation Principle section. There, we were discussing sending a
message via an SMS and an email system and how we could use interfaces to make sending
messages more robust. Consider the problem of creating and sending messages from a
different angle, consider how sending messages has changed over time. People have been
sending messages to one another since the beginning of humanity. A message is, after all, just a
communication “transmitted” from a sender to a receiver. Sometimes messages can have
metadata attached to them such as a priority, a title, a signature, or a timestamp. The notion of
what constitutes a message has evolved with technology but not by a lot. Now consider how
the way we transmit messages has changed over time. People have transmitted messages using
smoke signals, telegraph wires, horses, the postal service, email, text messages, and many more.
Clearly the notion of a message and the notion of transmitting a message have changed
independently of one another over time and we can predict that this trend may continue in the
future. This prediction would suggest that we should be separating the notion of a message into
one class and the delivery of the message into another. By Isolating these two things into their
own class we can isolate code that constructs or consumes a message from changes to the
delivery mechanism. We also then isolate code that is concerned only with message delivery
from changes to a message. This is a big testing win. By putting these responsibilities into
different classes we have reduced the number of things that must be tested when one or the
other change which to me is the whole point of the principle.
From the last section, this is the set of interfaces and classes we ended with:

1. public interface IMessage


2. {
3. IList<String> ToAddresses { get; set; }
4. string MessageBody { get; set; }
5. bool Send();
6. }
7.

Page 29
www.Intertech.com

8. public interface IEmailMessage:IMessage


9. {
10. string Subject { get; set; }
11. IList<String> BccAddresses { get; set; }
12. }
13.
14. public class SmtpMessage : IEmailMessage
15. {
16. public IList<String> ToAddresses { get; set; }
17. public IList<String> BccAddresses { get; set; }
18. public string MessageBody { get; set; }
19. public string Subject { get; set; }
20. public bool Send()
21. {
22. //Do the real work here
23. return true;
24. }
25. }
26.
27. public class SmsMessage : IMessage
28. {
29. public IList<String> ToAddresses { get; set; }
30. public string MessageBody { get; set; }
31. public bool Send()
32. {
33. //Do the real work here
34. return true;
35. }
36. }

Now if we apply the Single Responsibility Principle to our design we recognize that we should
not be mixing the Send method with the IMessage interface. We might come up with
something like this:

1. public interface IMessageServer


2. {
3. bool Send(IMessage message);
4. }
5.
6. public interface IMessage
7. {
8. IList<String> ToAddresses { get; set; }
9. string MessageBody { get; set; }
10. }
11.
12. public interface IEmailMessage : IMessage
13. {
14. string Subject { get; set; }
15. IList<String> BccAddresses { get; set; }
16. }

Page 30
www.Intertech.com

17.
18. public class SmtpMessage : IEmailMessage
19. {
20. public IList<String> ToAddresses { get; set; }
21. public IList<String> BccAddresses { get; set; }
22. public string MessageBody { get; set; }
23. public string Subject { get; set; }
24. }
25.
26. public class SmsMessage : IMessage
27. {
28. public IList<String> ToAddresses { get; set; }
29. public string MessageBody { get; set; }
30. }
31.
32. public class SmsMessageServer:IMessageServer
33. {
34. public bool Send(IMessage message)
35. {
36. //Do the real work here
37. return true;
38. }
39. }
40.
41. public class SmtpMessageServer : IMessageServer
42. {
43. public bool Send(IMessage message)
44. {
45. //Do the real work here
46. return true;
47. }
48. }

With this design, classes implementing the IMessageServer interface only need to concern
themselves with transporting the message and classes implementing the IMessage interface
only need to concern themselves with building a message. See how we tie these 2 things
together in the next section.

Page 31
www.Intertech.com

Dependency Inversion Principle


According to Wikipedia the Dependency Inversion Principle (popularized by Robert Martin)
states that:
• High-level modules should not depend upon low-level modules. Both should depend
upon abstractions.
• Abstractions should not depend upon details. Details should depend upon
abstractions.
In traditional architecture “Higher” level modules depend on “lower” level modules. If we think
in terms of an application with a presentation layer, an application layer, a business layer, and a
data layer. The Presentation layer is the highest layer and traditionally depends directly upon
and may communicate directly with the Application layer. The application layer is a higher level
layer than the Business layer and traditionally depends upon and communicates directly with
the business layer and so on.

When the Dependency Inversion Principle is applied this relationship is reversed. The
presentation layer defines the abstractions it needs to interact with an application layer. The
application layer defines the abstractions it needs from a business layer and the business layer
defines the abstractions it needs from a data access layer. That is a key departure from the more
classic approach, the higher layer defines the abstractions it needs to do its job and the lower
layers implement those abstractions.
A strict application of the principle may even put the abstractions in the layer defining them, for
example the presentation layer contains the presentation logic and application layer
abstractions (abstract classes or Interfaces), and the Application assembly contains application
logic and business layer abstractions and so on. In this application of the principle the Data
access layer depends upon the business layer, the business layer depends upon the application

Page 32
www.Intertech.com

layer and the application layer depends upon the presentation layer. The dependencies
(references) have been inverted thus the name of the principle.

That structure above feels a little bit funky to me so in all of my projects with any complexity I
usually end up with something like the following image. Presentation, Application, Business,
and DataAccess are in different assemblies. The interfaces will be in a different assembly or
assemblies as well.

Page 33
www.Intertech.com

The second part of the principle stating that abstractions should not depend upon details rather
details should depend upon abstractions historically solved a potential problem with C++
whereby header files could contain both the public and private functions and member variables
so in C++ this meant use a pure abstract class. In C# – a “pure” abstract class is an interface or an
abstract class with zero implementation.
What I like about this principle is that if I have followed the principle throughout my code base I
can test each layer in isolation by using Mocks, Fakes, test doubles etc. Here is a super simple
example. My company is in the business of building widgets. As a part of designing the
application I have decided that I need to log information and my first requirement is that I need
to be able to interact with a list of widgets transactionally. I create an Interface assembly and
add the following interfaces:

1. namespace Dip.Interfaces
2. {
3. public interface IWidget
4. {
5. int Length { get; set; }
6. int Width { get; set; }
7. bool DoWork();
8. }
9. }
10.
11.
12. namespace Dip.Interfaces
13. {
14. public interface ILogger
15. {
16.
17. //LoggingConcerns
18. bool LogMessage(string message);
19. bool LogMessage(string message, string callStack);
20.
21. }
22. }
23.
24. using System.Collections.Generic;
25.
26. namespace Dip.Interfaces
27. {
28. public interface ICoordinatingService
29. {
30. void CoordinateTransaction(IList<IWidget> widgets);
31.
32. }
33. }

Page 34
www.Intertech.com

For My data access layer I will only consider logging for brevity. I decide I need to be able to log
to a database or a file so I add a new project and create 2 classes as follows:

1. using Dip.Interfaces;
2.
3. namespace Dip.Storage
4. {
5. public class FileLogger:ILogger
6. {
7. public bool LogMessage(string message)
8. {
9. //write to file
10. return true;
11. }
12.
13. public bool LogMessage(string message, string callStack)
14. {
15. //write to file
16. return true;
17. }
18. }
19. }
20.
21. using Dip.Interfaces;
22.
23. namespace Dip.Storage
24. {
25. public class DbLogger:ILogger
26. {
27.
28. public bool LogMessage(string message)
29. {
30. //write to Database
31. return true;
32. }
33.
34. public bool LogMessage(string message, string callStack)
35. {
36. //write to Database
37. return true;
38. }
39. }
40. }

So far so good, Data access only depends on the interface assembly.


Now we add a business layer – this is typically the most important layer in that it is the layer that
brings the most value to the company, tends to have the most logic, and tends to end up having
the most bugs. We again start by adding a new project then add a widget class:

Page 35
www.Intertech.com

1. using Dip.Interfaces;
2.
3. namespace Dip.Business
4. {
5. public class Widget:IWidget
6. {
7. ILogger log;
8. //xtor injection
9. public Widget(ILogger logger)
10. {
11. log = logger;
12. }
13.
14. public bool DoWork()
15. {
16. //Execute business rules and log an entry
17. log.LogMessage("Hello World");
18. return true;
19. }
20. public int Length { get; set; }
21. public int Width { get; set; }
22. public string OtherStuffNotPersisted { get; set; }
23. }
24. }

Notice that to construct the widget I need to supply an ILogger – this is called constructor
injection. If the entire class doesn’t need to use a logger we could only add the ILogger
parameter to the methods that do need a logger – this is method injection. Again note, the
business does not reference the DataAccess layer directly – it only references the Interfaces
assembly. When I go to test the business layer I can use a mock implementation of ILogger
rather than rely on a database or the file system. This is the power of the dependency inversion
principle.
Continuing on we add our application layer. This layer tends to coordinate interactions between
existing business objects and maybe take care of transactions:

1. using Dip.Interfaces;
2. using System.Collections.Generic;
3.
4. namespace Dip.ApplicationService
5. {
6. public class CoordinatingService : ICoordinatingService
7. {
8. ILogger log;
9. public CoordinatingService(ILogger logger)
10. {
11. log = logger;
12. }

Page 36
www.Intertech.com

13.
14. public void CoordinateTransaction(IList<IWidget> widgets)
15. {
16. //Begin Transaction
17. foreach(var item in widgets)
18. {
19. item.DoWork();
20. }
21. //Commit...
22. }
23.
24. }
25. }

Again, we are using constructor injection to get our ILogger. In fact we are only relying on
IWidget and ILogger so this layer can be tested with mock versions of those objects and thus be
tested in isolation.
Now we move to the UI – I just used an MVC 4 project for this. Add a controller and I am
breaking the dependency inversion principle on purpose here:

1. using Dip.ApplicationService;
2. using Dip.Business;
3. using Dip.Interfaces;
4. using Dip.Storage;
5. using System.Collections.Generic;
6. using System.Web.Mvc;
7.
8. namespace Dip.UI.Controllers
9. {
10. public class WidgetController : Controller
11. {
12. //
13. // GET: /Widget/
14.
15. public ActionResult Index()
16. {
17.
18. //Call the service layer for this
19. var service = new CoordinatingService(new DbLogger());
20. //Normally this would be Extracted from the request
21. service.CoordinateTransaction(new List<IWidget>{
22. new Widget(new DbLogger()){Length=3,Width=4},
23. new Widget(new DbLogger()){Length=5,Width=6}
24. });
25.
26. var model = "success";
27.
28. //Send model to view
29. return View(model);

Page 37
www.Intertech.com

30. }
31.
32. }
33. }

How did I break the rule here? Several ways – I am referencing Application, business, and
the data access assemblies directly and instantiating those items right in the controller.
Why did I do this? Because the dependency Inversion Principle does not tell us how to
resolve dependencies it is used to guide us in a design that allows us to better test our
software in isolation. The above solution will allow us to test the application and business
layers in isolation. If we want to test our data access layer in isolation we would need to
refactor and better use interfaces or we would have to rely on the Microsoft Shim
framework to intercept calls to the database and file system and return some predictable
and repeatable results. With this design we have basically said “the UI layer is untestable in
isolation” and that may be good enough or that may be considered a fatal flaw. How might
we change the design of the UI layer to follow the dependency inversion principle? That is a
topic for another series but here is a hint, use a dependency injection container. With this
solution we will add a constructor to the controller that accepts an ILogger and
ICoordinatingService parameter. Additionally we will want to use a creational pattern which
will (using interfaces) expose a method that returns a Widget via the IWidget interface and
add that “factory” interface as a parameter to the constructor of the controller as well.

Page 38
www.Intertech.com

Delegation vs. Inheritance


Consider delegation over inheritance. I have had some pretty heated debates on this topic in
the past and I will even take it a step further and say the rule should state that we should favor
delegation over inheritance. Let’s consider the following classic (and intuitive)
person/student/teacher example. A class diagram might look something like this:

Person is our base class; Teacher and student derive from person. We can say that a student Is
A person and that a Teacher Is A person.
The up-side of this design is it is simple, intuitive, and it will perform well. The down side of this
approach is that there is a tight coupling between the derived classes and the base class, we are
breaking encapsulation by making the functionality of the derived class dependent on the
functionality in the base class, and we may even break encapsulation on our Person class by
using protected or public access modifiers where we normally wouldn’t do so.

The problem with breaking encapsulation is that making a change to one class (our base class)
can cause unintended changes in other consuming classes (our derived classes). I can’t begin to
tell you how many times I have seen a seemingly small change to a base class break huge pieces
of an application.

Page 39
www.Intertech.com

Now let’s consider using delegation rather than inheritance. We will change our class diagram
to look like the following:

We can say a teacher Has A person and similarly student Has A person.
The down-side of this design is that we have to write more code to create and manage the
person class and thus the code will not perform as well (though in most cases I suspect the
performance hit is negligible). We also cannot use polymorphism - we cannot treat a student as
a person and we cannot treat a teacher as a person. The up-side of this design is that we can
decrease coupling by defining a person interface and using the interface as the return type for
our Person property rather than the concrete Person class, and our design is more robust.
When we use delegation we can have a single instance of a person act as both a student and a
teacher. Try that in C# using the inheritance design!

Page 40
www.Intertech.com

Composition vs. Inheritance


Composition is a method we use to combine simple objects into more complex objects. With
composition we have an owner class that changes behavior by delegating the implementation of
certain behaviors to smaller and simpler objects. When we destroy the owner object, all of
these smaller objects the owner uses are also destroyed. If we were to use inheritance to
achieve this same goal (changing behavior) we would use a subclass to change the behavior of
the base class. As mentioned previously, the base class and subclass are tightly coupled and this
can tend to be a bit brittle.
Consider the very real case where we are writing an application that needs to work with both
Oracle and SQL Server. We could use inheritance to define a base class and have one subclass
for SQL Server and another for Oracle. Our application can figure out which object to instantiate
at runtime and that will work pretty well. Here is what our class diagram might look like:

Page 41
www.Intertech.com

If we take a little different approach and use composition instead we might have a class diagram
similar to the following:

The CompositionDatabaseWriter does not need to implement IDatabaseWriter but I did that
because I think it makes the example easier to understand. The application will use the
CompositionDatabaseWriter to do database work and CompositionDatabaseWriter will
determine whether to use SqlServerDatabaseWriter or OracleDatabaseWriter at runtime
perhaps by using a configuration file entry. When one of the CompositionDatabaseWriter
methods is called, CompositionDatabaseWriter simply calls the corresponding method on the
Sql Server or Oracle object.
Both designs allow us to interact with an oracle or Sql Server database which was our goal. Now
here is where the flexibility of composition comes in; suppose we now need to support a MySql
database. If we use inheritance to solve this problem, at a minimum we must create a
MySqlDatabaseWriter subclass, recompile, test, and deploy our data layer. If we use
composition instead we can create a MySqlDatabaseWriter in a new assembly and all we have
to do to use it is deploy this single assembly and change a config file. Very powerful, because
we used composition we can change the behavior at runtime via configuration.

Page 42
www.Intertech.com

The fact of the matter is that our sample is so simple we didn’t even use composition at all so
imagine the considerably more complex scenario where what we are doing is creating a
persistence object that must transactionally work with the file system and a database. We
define the persistence object and within the persistence object we delegate the responsibility of
dealing with the file system to one class, and dealing with the database to another. The
persistence object is composed of a FileSytemWriter and a DatabaseWriter. Our Persistence
object has only one concern – coordinating database and file persistence. Our FileSystemWriter
has only one concern – managing File System interactions, and finally our DatabaseWriter is only
concerned about interacting with a database of one type. We have decomposed a fairly
complex problem into a few smaller more managable problems, we came up with a nice robust
design thanks to composition and we have managed to do it in such a way that there are no SRP
violations either!

Page 43
www.Intertech.com

YAGNI – You Ain’t Gonna Need It


The YAGNI principle states that programmers should not implement functionality until it is
necessary. It is a pretty simple principle but oddly enough it is a difficult principle to follow.
I’ll take a crack at what I think the upside of breaking this rule is.
• It seems to me the biggest benefit to breaking this principle is that we can architect
and design our code by taking into account both current and future features thus
implementing a more robust design.
• Another big argument is that by implementing this feature our software will be
more useful. If our software is more useful we may sell more licenses or we may be
more productive.
Let’s look at the most obvious negatives of implementing a feature before you need it. This is a
longer list.
• If we don’t need it yet, maybe we should be spending our time and money
implementing something we do need now.
• If we implement a feature we should have some corresponding documentation
describing the feature – this takes time.
• If we implement something we need to test it -every release and this takes a lot of
time.
• When we add a feature in an agile/TDD shop, we need to write unit tests for the
feature so we can automatically test it every release. Writing the tests can take as
long as or longer than writing the feature and anyone who has to run unit tests
before a check in will probably agree that adding time to this automated process is
not good.
• When we add a feature we need to train the support crew and user base on how to
use the feature.
• A feature is hard enough to implement correctly when it is needed, how do we
expect to implement it optimally when we are still guessing whether or not we need
it?
A quick summary of the negatives: We are spending time writing code for something we might
need. We are writing the feature the way we think we will need it in the future. We are
spending time and money documenting and testing a feature that we are guessing we will need
and by the way we are also guessing how to best implement it.

Page 44
www.Intertech.com

If all of those (mostly financial) arguments aren’t enough to convince you, let’s take a look at
some down sides from an architect or developer’s point of view.
If code is in a release you have to assume it is being used. When it comes time to change the
code you think you needed, you are going to spend time figuring out how to change it without
breaking the (imagined?) user base. This might involve migrating data, configurations, backup
procedures, reports, integration work flows etc. When it comes time to change the code you
really do need you must consider all of the code. This includes the code you don’t realize that
you don’t need – if it is in production, how can you be confident in saying “oh, we really don’t
need that code”. This really stifles our ability to modernize our code. Even if you do find a way
to refactor the bloated code base not only will you have to change the existing code but you will
have to change the tests, documentation, and training materials.
Let the business analysts, user base, and marketing folks decide what features your product
needs and when it needs it. I would rather architect and design a system based on things that
are known. I don’t ever want to tell somebody that the system is the way it is because I thought
we needed some functionality that turns out to be useless. When we do a good job of designing
and reviewing our system we can be confident that we can refactor our code to implement
major functional changes when they are understood, needed, and the highest priority.

Page 45
www.Intertech.com

Consulting
Need help with a project? Intertech provides consulting on all the popular technologies
like .NET and Agile. Speak with one of our consultants today and let us help you.
 Intertech’s Agile Consulting Services
 Intertech’s .NET Consulting Services

Training
Interested in learning more about Agile or C#? Take a look at Interech’s multitude of
training courses:
 Interech’s Agile Training Courses
 Intertech’s C# Training Courses
 Intertech’s Training Schedule

Contact Information
Intertech, Inc
1575 Thomas Center Dr, Eagan, MN 55122
800.866.9884
651-288-7000

Page 46

You might also like