How to write clean code, practically

People often talk in the abstract about refactoring while writing code. But doesn’t that mean you just have a huge pull request with lots of unrelated changes muddled together? Or worse, several separate pull requests that conflict with each other?

It doesn’t have to be like that.

There’s an exercise I’ve used with great success which trains you to write code in the right order. It’s called Baby Steps. You start a 2 minute timer and start making your change whatever way seems sensible. When the timer goes off, if you have compiling code with green tests you may commit, otherwise revert. After all, you’re only losing 2 minutes of work. The idea is not to get faster at pressing the keys the next time around, it’s to learn what was blocking the change you tried to make, and focus in on that next iteration. You might need to keep note of why you’re doing various tasks and how they fit into the bigger picture – this methodology was formalized by the Mikado method.

When doing this, you’ll often find you’ve written a whole sequence of changes which are pure refactors*. You could pull request and merge them without actually changing the behavior of the application, but they change the shape of the code to make space for the feature/bugfix you’re currently working on. By the end of this process, your feature/bugfix can be a simple single-line fix. You should also find you hit the timer less often as you get used to identifying smaller problems mid-way through and resetting early. I’m not going to say you should always work to a 2 minute timer, but I’d certainly advocate the overall style of development. Taking turns at driving when the timer goes off works great in a pair to keep you both engaged. Note that this doesn’t replace other methodologies. If it’s a big change, spike it. Still use TDD, BDD or whatever you’re using this week to drive your development.

To summarize, use pure refactors* first to make space for your the changes you’re working on. Then make your behavior changes separately. Baby Steps is a great way to practice doing this that works on real code.

Refactoring an API? Try Expand, Migrate, Contract refactoring.

Listing .NET assembly versions in powershell

Sometimes in .NET you end up in assembly version hell. When you’re trying to get your assembly references and binding redirects right, this function can really come in handy, it works directly on a file, or on directories and has the recurse switch available.

function Get-AssemblyName($path, [Switch]$Recurse) {
if ((Get-Item $path) -is [System.IO.DirectoryInfo]) { Return Get-ChildItem -Path$path -Filter '*.dll' -recurse:$Recurse | % { Get-AssemblyName$_.FullName }
}
$fileVersion = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($path).FileVersion

Try {
$name = [System.Reflection.AssemblyName]::GetAssemblyName($path)
$publicKeyToken = "null" if ($name.GetPublicKeyToken()) {
$publicKeyTokenParts =$name.GetPublicKeyToken() | % { $_.ToString("x2")}$publicKeyToken = [String]::Join("", $publicKeyTokenParts) }$bindingRedirect =
@"
<dependentAssembly>
<assemblyIdentity name="$($name.Name)" publicKeyToken="$publicKeyToken" /> <bindingRedirect oldVersion="0.0.0.0-$($name.Version)" newVersion="$($name.Version)" /> </dependentAssembly> "@ Return New-Object –TypeName PSObject –Prop ([ordered]@{Name=$name.Name; NetVersion=$name.Version; PublicKeyToken=$publicKeyToken; FileVersion=$fileVersion; Path=$path; DotNet=$true; BindingRedirect=$bindingRedirect})

} Catch [System.BadImageFormatException] {
$fileName = (Get-Item$path).BaseName
$fileVersion = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($path).FileVersion
Return New-Object –TypeName PSObject –Prop ([ordered]@{Name=$fileName; FileVersion=$fileVersion; Path=$path; DotNet=$false})
}
}


Fixing assembly version errors

If you write the generated redirects to a file, it’s easy to copy them into your web/app.config:
(Get-AssemblyName -Recurse “Path\to\bin”).BindingRedirect > out.txt

I’ve found assembly version hell happens much more often to me since I started trying to use .NET standard on .NET framework 4.6.1 because it’s only really supported as a hack. .NET framework 4.6.1 doesn’t contain all the assemblies for .NET standard 2.0 because .NET standard 2.0 didn’t exist when the framework version shipped, but it is still said to “support” it because it’s possible to use NuGet to pull in the required references.

However, this usually requires an explicity reference to the .NET standard dll in the sdk:

<Reference Include="netstandard">
<HintPath>$(MSBuildSDKsPath)\..\Microsoft\Microsoft.NET.Build.Extensions\net461\lib\netstandard.dll</HintPath> </Reference>  I also defined MSBuildSDKsPath on TeamCity as a root level environment variable so that it was present for all my builds. You’ll then need to get the right NuGet packages and the the right assembly binding redirects to the version that’s in those packages. But even after doing all that you might still get runtime errors, like: [FileNotFoundException: Could not load file or assembly ‘System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’ or one of its dependencies. The system cannot find the file specified.] System.Web.Http.GlobalConfiguration..cctor() +0 One cause of this, is if the machine doing the build actually built against a later version of the .NET framework, which did contain the dlls you NuGetted. If Microsoft had shipped those latest versions to NuGet, we could just use them, binding redirect to them, and everything would be fine, but alas, the did stopped shipping updates to the NuGet packages once they were in proper framework releases! So on build machines that have those later versions you need to very carefully avoid the framework version being copied, otherwise your binding redirects will point to the wrong version. That’s where the PowerShell function at the top comes in – use it to find which project’s bin directory has the wrong version in, and then trace back where it came from and eliminate it. i.e. Make sure the only reference to the culprit in that project is to the NuGet package (no plain assembly references), and add a binding redirect if something in that project wants a different version. Of course the real solution is for us to upgrade all our build tools, servers and any customer machines to .NET Framework 4.7.1 which does fully support .NET standard 2.0, and stop all of this insanity, but that’s going to take anywhere between a few weeks and a few years depending on the product, so for now this is what we’re stuck with. Thankfully the new Visual Studio 2017 project format by default doesn’t add the GAC to the assembly search paths in a build – see this GitHub issue. So if you use that, you’ll probably avoid accidentally copying a file from your build machine at least. If you can’t move to that yet, it’s also possible to manually set the assembly search paths in each project file (or a common props file) without using the new format. Posted in Build, C#, Note to self | | Leave a comment PS Remove directory atomically Remove-Item -recurse in Powershell isn’t atomic. If a file is locked in the directory, you’ll end up with half of the files deleted, but not the locked one. Sometimes that just isn’t ok, like during an octopus deployment, so I use this at the start: function RemoveAtomically-Directory($currentFoldername) {
$currentFoldername =$currentFoldername.TrimEnd('/','\')
$timestamp = Get-Date -format 'yy-MM-ddTHH-mm'$newFolderName = $currentFoldername + "_" +$timestamp
Rename-Item $currentFoldername$newFolderName #Atomic
Remove-Item $newFolderName -Recurse }  Posted in Note to self | Tagged , | Leave a comment Download packages from Octopus It’s not recommended to download packages from Octopus programmatically. You should set up a separate feed instead ideally. But…if you really need to right now and don’t have time for that, chuck this into a Download-Package.ps1 file: Param($OctopusUrl, $ApiKey,$PackageName)

$headers = @{"X-Octopus-ApiKey"=$ApiKey}
$listPackageUrl =$OctopusUrl + "/api/packages?nuGetPackageId=$PackageName&take=1"$listResponse = Invoke-WebRequest $listPackageUrl -Headers$headers
$latestPackageMetadata = ($listResponse | ConvertFrom-Json).Items[0]
$rawUrl =$latestPackageMetadata.Links.Raw
$fileExtension =$latestPackageMetadata.FileExtension

$packageUrl =$OctopusUrl + $rawUrl Invoke-WebRequest$packageUrl -Headers $headers -OutFile "$PackageName\$fileExtension"