# Tuesday, May 01, 2007

I was trying to come up with a set of guiding principles as we've been using them on my current project, with an eye toward educating new developers being assigned to the project, so I wrote up a PPT deck for the purpose.  Scott thought it would make a good, general purpose manifesto for any project, so here goes...

These are my opinions based on experience and preference, YMMV.  Much of this is common sense, some of it is arbitrary, but it's all stuff that I think is true.

  • Our "guiding principles"
    • Test Driven Development
      • As much as possible, all of our code should be developed according to the TDD methodology.  If we stick with it, we will always have confidence in the current build (i.e. that it really works).
    • Continuous Integration
      • We want our build to produce something that looks as much as possible like a production deployment.  To that end, our build deploys a real live system and runs "integration" tests against it. 
  • Unit tests
    • Two kinds of tests
      • Unit tests have no dependencies, or only depend on other assemblies owned by us, which can be mocked.  This means our unit tests should run on anyone's box without having to set up things like SQL, ADAM, etc.
      • Integration tests depend on code not owned by us, like SQL, ADAM, IIS, etc. 
    • Automation is equally possible for both sets of tests
      • NUnit/MbUnit for all of the tests
      • MbUnit allows for test "categories" like "unit" and "integration" and can run one at a time
      • Unit tests run on every build, integration tests run at night since they take much longer to run
    • All UI development should follow the MVP pattern for ease of testing
      • This includes Win/WebForms, MMC snapins, etc.
  • Test coverage
    • 90%+ is the goal
      • This is largely achievable using proper mocking
    • NCover runs as part of the build, and reports are generated
  • Buy, not build
    • Take full advantage of the platform, even if it only solves the 80% case
      • every line of code we don't have to write and maintain saves us time and money
    • Don't write a single line of code you don't have to
    • Take full advantage of .NET 3.0, SQL 2005, Windows 2003 Server, plan for- and test on Longhorn
    • Don't invent new solutions to solved problems
      • Even if you know how to write a better hashing algorithm than the ones in the BCL, it doesn't add value to your product, so it's not worth your time
  • Limit compile time dependencies on code you don't own
    • Everything not owned by us should be behind an interface and a factory method
      • Easier to mock
      • Easier to replace
    • ILoggingService could use log4net today, EnterpriseLibrary tomorrow, but clients are insulated from the change at compile time
  • Define your data contracts in C# (think "active record")
    • All persistent storage should be abstracted using logical interfaces
      • IUserProfileService takes a user profile data object, rather than methods that take parameter lists
      • IUserProfileService knows how to store and retrieve User Profile objects in ways important to your application
      • How a User Profile object gets persisted across databases/tables/XML/whatever is only interesting to the DBA, not consumers of the service
      • This means database implementations can change, or be optimized by DBAs without affecting the app
      • The application doesn't care about how database file groups or clustered indexes work, so define the contract, and leave that bit to a DBA
  • Fewer assemblies is better
    • There should be a VERY good reason for creating a new assembly
    • The assembly is the smallest deployable unit, so it's only worth creating a new assembly if it means NOT shipping something else
    • Namespace != assembly name.  Roll up many namespaces into one physical assembly if they all must be deployed together.
  • Only the public interface should be public
    • Only make classes and interfaces public if absolutely necessary
    • Test code should take advantage of InternalsVisibleTo attributes
    • VS 2005 defaults to creating internal, not public classes for a reason
    • If it's public, you have to support it for ever
  • Windows authentication (good)
    • Just say no to connection strings
    • Windows authentication should be used to talk to SQL, ADAM, the file system, etc.
    • You can take advantage of impersonation without impersonating end users
      • Rather than impersonating end users all the way back to the DB, which is expensive and hard to manage, pick 3-4 windows accounts like "low privilege", "authenticated user", and "admin" and assign physical database access to tables/views/stored procs for those windows accounts
      • When users are authenticated, impersonate one of those accounts (low priv for anonymous users, authenticated user for same, and admin for customer service reps, for example)
  • Tracing
    • Think long and hard about trace levels
      • Critical is for problems that mean you can't continue (app domain is going away)
      • Error means anything that broke a contract (more on that later)
      • Warning is for problems that don't break the contract
      • Info is for a notable event related to the application
      • Verbose is for "got here" debugging, not application issues
    • Use formatted resource strings everywhere for localization
    • For critical, error, or warning, your audience is not a developer
      • Make error messages actionable (they should tell the recipient what to do or check for to solve the problem)
  • Error handling
    • Method names are verbs
      • the verb is a contract, e.g. Transfer() should cause a transfer to occur
    • If anything breaks the contract, throw an exception
      • if Transfer() can't transfer, for whatever reason, throw
      • Exceptions aren't just for "exceptional" conditions.  If they were really exceptional, we wouldn't be able to plan for them.
    • Never catch Exception
      • If it's not a problem you know about, delegate up the call stack, because you don't know what to do about it anyway
    • Wrap implementation specific exceptions in more meaningful Exceptions
      • e.g. if the config subsystem can't find the XML file it's looking for, it should throw ConfigurationInitializationException, not FileNotFoundException, because the caller doesn't care about the implementation.  If you swap out XML files for the database, the caller won't have to start catching DatabaseConnectionException instead of FileNotFoundException because of the wrapper.
      • Anytime you catch anything, log it
        • give details about the error, and make them actionable if appropriate
      • Rethrow if appropriate, or wrap and use InnerException, so you don't lose the call stack
  • The definition of "done" (or, how do I know when a task is ready for QA?)
    • Any significant design decisions have been discussed and approved by the team
    • For each MyClass, there is a corresponding MyClassFixture in the corresponding test assembly
    • MyClassFixture exercises all of the functionality of MyClass (and nothing else)
    • Code coverage of MyClass is >=90%, excluding only lines you are confident are unreasonable to test
      • yes this is hard, but it's SOOOOO worth the effort
    • No compiler warnings are generated by the new code
      • we compile with warnings as errors.  if it's really not a valid or important warning, use #pragma warning disable/enable
    • Before committing anything to source control, update to get all recent changes, and make sure all unit and integration tests pass
    • FxCop should generate no new warnings for your new code
    • Compiling with warnings as errors will flush out any place you forgot documentation comments, which must be included for any new code
  • There WAS a second shooter on the grassy knoll
    • possibly one on the railway overpass
    • (that's for anyone who bothered to read this far...)
Friday, May 04, 2007 7:52:40 AM (Pacific Daylight Time, UTC-07:00)
Good stuff, Patrick. I hope you don't mind - I used your manifesto as a basis for discussion in my developer team meeting this week. :)
Thursday, May 24, 2007 9:28:46 AM (Pacific Daylight Time, UTC-07:00)
Fantastic list! This is a great guideline for .NET projects.
Friday, May 25, 2007 6:10:53 AM (Pacific Daylight Time, UTC-07:00)
Churlish as it is to correct an article this excellent, I'd like to point out two spelling errors:

IUserProvileService should (I presume ;-) ) be IUserProfileService

"effecting the app" should be "affecting the app" Trust me on this one...

Anyway, I hope a lot more people read this, I love it.
Julian Birch
Saturday, May 26, 2007 8:35:43 AM (Pacific Daylight Time, UTC-07:00)
Great list.
Can you expand on "Just say no to connection strings"? When I first read it, I thought "right on, don't make me have to store or encrypt passwords". Until I read that you recommended using multiple impersonation accounts, depending on the user. So I'm assuming that means you are not using the Windows account that the process executes under (since you need multiple accounts), which means you need to store credentials for the multiple Windows accounts... which brings us back to solving the storage/encryption of passwords problem.
What are you gaining (or, what am I missing)?
Saturday, May 26, 2007 4:50:12 PM (Pacific Daylight Time, UTC-07:00)
aha..i always *knew* there was a second gunman..lol
Tuesday, May 29, 2007 2:22:14 PM (Pacific Daylight Time, UTC-07:00)
I'm actually not sure on the details of how the multiple accounts thing is going to work. We may set up an entry point on the server side that sets the right impersonated account. You are right that this implies storing credentials someplace. I'm thinking we might encrypt the credentials via DPAPI, which provides a pretty simple way to store and retrieve secrets, though of course that has issues as well. If we can't do it in a way that requires a bunch of extra config, we many drop the multiple accounts idea.
Thursday, June 14, 2007 6:14:24 PM (Pacific Daylight Time, UTC-07:00)
Nice article.
Re Joshua and connection strings, I like having a middle tier COM+ application running as a local user be the only user allowed to access the db. This user is create by the installer, has no interactive login rights, and the password is random and is only known to the Windows Service Control Manager. On the db, this user has only rights to execute my stored procs to avoid risk of sql injection.

We then set the access rights to this COM+ app to allow a client app (running in a valid users account) to access the api as needed. With this in place, we store no passwords and no connection strings.
jerry albro
Thursday, June 14, 2007 6:35:20 PM (Pacific Daylight Time, UTC-07:00)
Love the list. And there was at least one other shooter. No doubt about it!
Friday, June 22, 2007 11:18:03 PM (Pacific Daylight Time, UTC-07:00)
I use this definition of done:

- The code should compile
- Have a simple design with fewest classes
- Be well factored - have no duplication
- Structured in accordance with coding standards
- Well documented
- Communicate the developer’s intentions
- Checked into the version control repository
- Integrated and build successfully.
- Unit tests must pass
- Code coverage >85%

Some of points are fuzzy and can’t be measured, but it forces the developers thing about it.
Friday, July 27, 2007 1:47:50 AM (Pacific Daylight Time, UTC-07:00)
- Exceptions aren't just for "exceptional" conditions. If they were really exceptional, we wouldn't be able to plan for them.

Not at all, exceptions are exceptional, that's why they're called Exceptions. Of course Java (and C# therefore) screwed this up by confusing them with errors, but that's no excuse to maintain the problem.

You can plan for exceptional circumstances, (eg. I have health insurance in case one day I get ill - but that illness is an exceptional circumstance, not something I even think about day to day). and that's the point, an exception is for code reliability when something you assumed would never happen happens (eg you fail to connect to the network: you never think about this happening, but you don't want your app to just die on the user, and you don't want to have to litter error codes about the place. So an exception that pops a message would be fine - my hypothetical app here is unusable without the network, other exceptions may require different configurations).

So please, get rid of that bit, make your code better by not using exceptions as errors.
Comments are closed.