How do you organize your code?
How do we organize our code? Well, it all depends on the project and what we're trying to accomplish. We're also always learning. Our big C++ app builds differently than our lightweight C# stuff. However, for the most part we start with some basic first principles.
- A repeatable build
- Obvious, maybe, but if you can't build it n times and get the same result, you may have some problems.
- Continuous Integration
- When possible and appropriate, have builds automated and build mail sent out.
- We started with building on every check-in, and for the most part we do that.
- We added a daily build, so now we will build on every check-in, but there's a 5pm build also.
- CruiseControl.NET - the goal being a company-wide dashboard.
- NAnt -building
- NUnit - testing
- NCover - code coverage, not formalized yet.
- FxCop - For the obvious stuff, CLS compliance, etc.
- NDoc - the shiznit. Use it to automate CHM file creation. That auto-generated CHM is then sucked into a larger 'prose' CHM.
- libcheck - looking into it. Using it to measure API churn.
As far as layout (picture above), there's no right way. We've got folks who've been doing builds since before Windows, so we have a little *nix-y style in a few things. Again, if it works, use it. I like:
- source - for source. duh.
- tools - This CVS module is the source for any tools/utils the project needs. Shared between projects.
- install - XCopy deployment, it'd be nice. We do versioned MSIs for everything significant.
- test - Not Unit-tests per se, mostly larger projects for integration and regression testing.
As far as the build directory is concerned:
Everything under build is 'important.' It kind of speaks for itself. Bin for bins, doc for the CHM.
In source, we have:
Every 'subsystem' has a folder, and usally (not always) a Subsystem is a bigger namespace. In this picture there's "Common" and it might have 5 projects under it. There's a peer Common.Test. Under CodeGeneration, for example, there's 14 projects. Seven are sub-projects within the CodeGeneration subsystem, and seven are the NUnit tests for each. The reason that you see Common and Common.Test at this stuff is that they are the 'highest." But the pattern follows as you traverse the depths. Each subsystem has it's own NAnt build file, and these are used by the primary default.build.
In this souce directory we've got things like build.bat and buildpatch.bat. The goal being that folks can get the stuff from source control and type BUILD and be somewhere useful. It's VERY comforting to be able to reliably and simply build an entire system. Recently I had to patch something I'd worked on 18 months ago, but that had used this principal. I did a Fet, did a BUILD and I was cool. We then used reflector and Assembly Diff to confirm that the patch surface area was minimal.
We DO build things in VS.NET, but for the vast majority of projects, we prefer a command-line build to get real repeatabilty. With .user files and per-user reference paths, I just don't trust VS.NET to do the right thing.
Another thing we make a lot of use of are what we call Junctions. These are NTFS Reparse Points, also known as symlinks.
In this screenshot of a standard DIR from the command prompt, directories show up as <DIR> while junctions say <JUNCTION>. These junctions point to other directories. But NTFS makes it transparent; you can CD into them, and even Explorer has no clue. Certainly, they can be dangerous, as you can clean out a directory and not realize that it's the authoritative source for something and is being referenced by other projects.
You can download Junction from SysInternals. You can also use LINKD, that shipped with the Windows 2000 Resource Kit. In this screen shot, the relative SDK folder points to an installed version of our SDK. This allows our CSPROJ files to refer to our assemblies with relative paths, gives developers a lot more flexibilty, and makes the NAnt builds simpler.
It also allows us to swap out different versions of things. For example, I could point the SDK folder to a previous (or future!) build of the SDK and build again. This flexibility comes with a cost though - complexity. People have to think a little more. Fortunately, our build guys are pretty savvy and will (shell script-like) go looking for preferred versions of things in standard locations and set up Junctions for folks.