NDepend

Improve your .NET code quality with NDepend

Advices to Become a Remote Programmer

With the actual COVID-19 worldwide outbreak many programmers are already forced to work remote, and we can expect that most of us will soon be home.

In the IT and especially in the development industry we are lucky enough: the bulk of our work can be done remotely. I guess many of us will enjoy changing their daily routine will less social obligations and more time spent within the IDE.

I am working remotely for 15 years so here are some advices I’d give.

Care for your Workspace

Most passionate programmers already have a workspace at home. I won’t go through the invest in great hardware advice because as a programmer you certainly have thought about all this. However it is certainly time to improve your workspace.

The number one move for me has been to work in a room different than the bedroom. Your brain needs to identify clearly where to code and where to rest. Mulling over programming problems is common outside the work office, but working at home bring that to a different level. Unfortunately working outside of the bedroom is not always practicable, especially taking account that kids will likely have to stay at home if the government’s choose to close schools as they already did in several countries. If you can free-up working time in a room different than the one where kids are staying, it is time to invest in noise cancelling headphone that can be combined with a pair of earplugs. Custom silicone earplugs (made for my ears by a professional audioprosthetist) combined with my Bose QuietComfort lead to great noise reduction.

When working at home reducing distraction is a major challenge. If one of your window has a nicer view, or at least a clear sky view, it is worth working near and turn the head 90 degrees for micro-pauses. If possible work in a room where there is no TV nor game console. However the biggest source of distraction are chats, meetings, social networks, mails, news, youtube.. Of course some time must be dedicated to all those but I advise to be radical: turn-off the WIFI when it is not time to go online. This is inspired from some of my most productive coding sessions that took place in planes. Not being able to access github or stackoverflow can be problematic so you can be less radical by logging-off from all social media and use the browser in incognito mode: the key is to voluntary stay away from temptation. The more you resist, the easier it gets to resist.

Having a secondary informal workspace can help, especially if you must stay home. Working on a couch for one or two hours with my laptop often leads to quite productive coding sessions. But avoid working in your bed: your brain will associate bed with work and you’ll get hard time to fall asleep.

Structure your daily routine

Identify the time of the day when you are the most productive and structure your daily routine around it. You must agree with your company when you’ll be available online for virtual chats and meetings. If they are not flexible on social work schedule, insist to get some chunks of uninterrupted time. Working at home is your chance to avoid being interrupted during your peak coding hours and you might be well experiencing at home some of your highest productivity time.

There are some times of the day that deserve particular attention:

  • Starting the working day, here is a tip recently shared by Scott Hanselman. More generally I’d advise to do anything non-digital for 20 to 40 minutes before getting started (exercise, walk, time with family, breakfast, mindfulness meditation…). Also keep in mind that getting started is the hardest part: be especially careful of what you are doing during the first minutes of your working day, this will determine your productivity for the next hours.

  • Lunchtime: if possible this is the time to go outside and meet people. Also you’ll benefit from daylight which is an important component for good sleep at night.
  • After lunch: a 10/15 minutes nap can help a lot to counter the digestion tiredness, to start the afternoon fresh and focused.
  • Log-off time: unless you are the kind of programmers that gets hyper-productive during the night, it is important to decide ahead when you’ll log-off. Coding is a quite an addictive activity that can ruin your evenings, your sleep cycle and your social life.

Communicate

You are certainly already using a remote code hosting platform like github. But there are many coding situations where a short face-to-face video chat is more efficient than hours of back and forth over. This necessity needs to be acknowledged by all team members and you must agree ahead which situations deserve a synchronous interruption.

Also everyone is different and you should be aware (and take account) of communication mediums favored by each of your colleague.

Trust plays a big role in a team of remote developers. It is a good habit to advocate yourself by explaining what you’ve done and ask for feedback. If for some reason you estimate that you don’t progress as quickly as expected, discuss with others. However talking too much about yourself can quickly become bothering for others: find the right balance and also take some extra care to listen to others.

Ask for code reviews and review your peer code, especially if reviewing code is not part of your team habits. Keep in mind that the code itself is a great way to communicate with other developers.

Care for Yourself

By losing your daily routine at work it is easy to become a sloth potato, especially if you are living alone. This is why you should take care of yourself by abiding by a number of no-brainers routines:

  • Keep your shower and shave time.
  • Get dressed. Even better, dedicate some comfortable clothes for work.
  • Exercise at the same time everyday. It is a great mindset trick to consider exercise as an integral part of your work to never miss a session.
  • Take care of what you are eating, no need to ramble on how important is quality food.
  • If you can go out, go out at least an hour every day. Else make sure to take some daylight from your window.
  • Get started with mindfulness meditation. The goal is to step back and watch yourself working most of the time. This way when your mind slides to a social network or procrastinate you know it is time to take a break.
  • Ideally you should anticipate when you’ll take breaks.
  • Make sure you sleep enough, at least 7 hours a day, 8h preferably. I know some developers that sleep less to work more but they are ruining their health and their long-term productivity. This is not a judgment but a scientific prediction.
  • If you are working alone at home for weeks you’ll quickly feel lonely. Be aware of this threat and do everything you can to fight it: go out and meet your friends and relatives if possible, else do some video chats with them. Listening to music while coding can help (personally this kind of music bring me quickly in the zone).
  • Before having children I enjoyed working during the weekend with no email interruption, and do weekend’s stuff during the week to avoid the crowd. By now my weekends are entirely dedicated to family and social time and even during busy period I don’t sacrifice them. It is really up to you to decide to work at night and/or during weekends but keep in mind that the standard work hours scheme exists for a reason. The key to all great achievements is to take some rest at the right pace.

Conclusion

We’re facing weird time and we can only expect worse for the weeks to come. Many professional programmers will experience for the first time remote work. The good news is that mathematic tells us that the COVID-19 worldwide outbreak will curve down by this spring. Take care.

 

 

 

Don’t rely on someone else to protect your software

This morning I stumbled on this post Decompilation of C# code made easy with Visual Studio on the Visual Studio blog. Basically VS will soon be able to not only decompile third-party code but also generate some sort of PDB information that will make the decompiled code debuggable. The promise is no more  “No Symbols Loaded” or “Source Not Found” from within debugging session in VS and personally I found this awesome.

However most of this post’s comments are like “how do I protect my code from being decompiled then?!” and “Microsoft does not care about its customers’ intellectual property.”. These comments are absurd. Since its inception in 2002 .NET compiled code can be decompiled and read crystal clear with popular tools like .NET Reflector, IL Spy, dotPeak… This is a direct consequence of having IL/byte code and a CLR with a JIT compiler. I wonder to which extend those that wrote these comments are aware of that?

Protect your Intellectual Property

The first step is to make sure that your EULA forbid from decompiling your code, something like:  Licensee may not reverse-engineer, decompile, disassemble, modify, or translate the Product, or make any attempt to discover the source code of the Product;

The second step is to obfuscate your compiled code. Since 2002 those that want to protect their compiled code from decompilation just have to obfuscate it. There are mature free tools like ConfuserEx and paid tools available. This is what we do within our .NET shop since 2007 with success.

Also with .net native and AOT (Ahead of Time compilation) one can add a whole new layer of complexity by skipping the IL code and compile directly to machine code (e.g. X64 instructions).

But keep in mind that Obfuscators and AOT only protect the intellectual property to some extent. Your code best kept secrets are still executable in both scenarios, it means they are still there. Someone skilled ready to spend a large amount of time to reverse engineer your code can still have access to your intellectual property.

The ultimate way to protect your intellectual property is to provide your services as online SaaS. This way nobody will ever have access to your code. For example the whole SEO industry is based on guessing what Google and others web search engine are doing. These algorithms are protected because nobody except Google employees have access to them. In many scenarios Saas means also that one forces his clients to share their sensitive data, since they are processed on one’s server, and in many scenario this is not applicable. By spying your data Google and Facebook knows you better than you do, but it seems that only a fraction of the humanity disagree with that. Ok I digressed…

Protect your software from hackers

Keep in mind that Obfuscators and AOT don’t protect your code from hackers. It is still easy for any solid hacker to crack your license-checking-layer and provide a free version of your software online as a warez. The only possible protection from hackers that have access to your code are integrity checks. An integrity check is made of two parts:

  1. A layer that detects if some bytes of your compiled code have been tweaked (typically with a custom or standard hash function)
  2. A subtle malfunction of your software that prevents to use it if the compiled code has been tweaked.

The whole point of an integrity check is to consume the time of the hacker. This is why the word subtle is in bold, the malfunction is not a dumb exception to provoke a fail-fast, the malfunction must be something that appears minutes after the integrity check failed and finally makes the software totally unusable (like a massive memory leak – clearing some data – freezing some UIs – firing a timer to close the user session after a random number of minutes…)

By multiplying the integrity checks and the subtle corresponding malfunctions one can only hope to discourage a talented hackers to waste days, weeks or months to crack his software. But be aware that these guys are primarily driven by challenges… The good news is that with .NET there are tons of possible ways to write subtle integrity checks.

Conclusion

Protecting the intellectual property and the software itself is a difficult task. Don’t complain to Microsoft or anyone else that they should offer an out-of-the-box tool for that. If such a mainstream protection tool existed it would be a challenge for the best talented hackers and it wouldn’t resist long anyway.

The question you should ask for is: is it worth spending resources to protect my assets instead of spending these resources to make my paid clients even more happy. This is a difficult trade-off that must be carefully thought out. But always keep in mind that you must not rely on someone else to protect your software.

Mythical man month : 10 lines per developer day

The mythical book, Mythical man month quotes that no matter the programming language chosen, a professional developer will write on average 10 lines of code (LoC) day.

After 14 years of full-time development on the tool NDepend I’d like to elaborate a bit here.

Let’s start with the definition of logical Line of Code. Basically, a logical LoC is a PDB sequence point except sequence points corresponding to opening and closing method brace. So here we have a 5 logical lines of code method for example:

I already hear readers complaining that LoC has nothing to do with productivity. Bill Gates once said “Measuring software productivity by lines of code is like measuring progress on an airplane by how much it weighs.“.

And indeed, measured on a few days or a few weeks range, LoC has nothing to do with productivity. As a full-time developer some days I write 200 LoC in a row, some days I spend 8 hours fixing a pesky bug by not even adding a LoC. Some day I clean dead code and remove some LoC. Some other days I refactor existing code without, all in all, adding a single LoC. Some days I create a large and complex UI control and the editor generates automatically 300 additional LoC. Some days are dedicated solely to performance enhancement or writing tests…

What is interesting is the average number of LoC obtained from the long term. And if I do the simple math our average is around 80 LoC per day. Let’s precise that we are strict on high code quality standard both in terms of code structure and formatting, and in terms of testing and code coverage ratio (see the last picture of this post that shows the NDepend code coverage map). For a code quality tool for developers, being strict on code quality means dogfooding☺.

So this average score of 80 LoC produced per day doesn’t sacrifice to code quality, and is a sustainable rhythm. Things get interesting with LoC after calibration: caring about counting LoC becomes an accurate estimation tool. After coding and measuring dozens of features achieved in this particular context of development, the size of any feature can be estimated accurately in terms of LoC. Hence with simple math, the time it’ll take to deliver a feature to production can be accurately estimated. To illustrate this fact, here is a decorated treemap view of the NDepend code base, K means 1.000 LoC. This view is obtained from the NDepend metric view panel with handmade coloring to illustrate my point. The small rectangles are methods grouped by parent classes, parent namespaces and parent assemblies. A rectangle area is proportional to the corresponding method #LoC.

treemap code-metric

Thanks to this map, I can compare the size in terms of LoC of most components. Coupling this information with the fact that the average coding score if 80 LoC per day, and looking back on cost in times for each component, we have an accurate method to tune our way of coding and estimate future schedules.

Of course not all components are equals. Most of them are the result of a long evolutive coding process. For example, the code model had undergone much more refactoring since the beginning than say, the dependency matrix for example that had been delivered out-of-the-box after a few months of development.

This picture reveals something else interesting. We can see that all these years spent polishing the tool to meet high professional standards in terms of ergonomy and performance, consumed actually quite a few LoC. Obviously building a performant code query engine based of C# LINQ that is now the backbone of the product took years. This feature alone now weights 34K LoC. More surprisingly just having a clean Project Properties UI management and model takes (model + UI) =(4K + 7K) = 11K LoC. While a flagship feature such as the interactive Dependency Graph only consumes 8K LoC, not as much as the Project Properties implementation. Of course the interactive Dependency Graph capitalizes a lot on the existing infrastructure developed for other features including the Dependency Model. But as a matter of fact, it took the same amount of effort to develop  the Dependency Graph than to develop a polished Project Properties model and UI.

All this confirms an essential lesson for everyone in charge of an ISV. It is lightweight and easy to develop a nice and flashy prototype application that’ll bring enthusiast users. What is really costly is to transform it into something usable, stable, clean, fast with all possible ergonomy candy to make the life of the user easier. And these are all these non-functional requirements that will make the difference between a product used by a few dozens of enthusiast users only, and a product used by the mass.

To finish, it is also interesting to visualize the code base through the prism of code coverage ratio. The NDepend code base being 86% covered, by comparing both pictures we can easily see which part is almost 100% covered and which part need more testing effort.

Not planning now to migrate your .NET 4.8 legacy, is certainly a mistake

2020 will see the achievement of the massive remodeling of the .NET platform initiated by Microsoft in November 2014 with the introduction of .NET Core 1, with the promise of an open-source, a multi-platform and a modernizable framework (thanks to no rock-solid backward compatibility constraint) – everything that the .NET Framework isn’t. This U-turn in the Microsoft plans for .NET is part of the new Microsoft’s strategy initiated by Satya Nadella that became CEO of Microsoft in February 2014, succeeding to Steve Ballmer.

.NET 5 will be released in November this year. Within 6 years Microsoft will have succeeded a complete platform shift like no other. The .NET Core brand was here to make clear that two .NET platforms were living side by side. But now we know that the .NET Framework 4.8 won’t evolve anymore and that all Microsoft efforts will be put on .NET Core continuation with well scheduled releases ahead. Let’s use the term .NET OSS to designate [.NET Core, .NET 5, .NET 6…. [ in the remainder of this post.

We can expect an early beta of .NET 5 before July 2020. Today in January 2020 the .NET 5.0 milestone is 72% achieved.

By now, the way to get prepared to .NET 5 and later is to migrate to .NET Core 3.1. Despite the branding change from .NET Core 3.1 to .NET 5 it is no mystery that .NET 5 will be mostly based on the actual .NET Core platform.

The cost of migration from .NET 4.8 to .NET OSS can get pretty high, especially if the legacy relies on some deprecated APIS (like WCF, WWF, WebForms or AppDomain). Thus it may seems attractive to stick with .NET 4.8 if your application is intended to run on Windows only.

Why it is not a good idea to not anticipate the migration now?

.NET 4.8 won’t evolve but some security patches will be provided for as long as we can foresee. However we can predict that .NET 4.8 will be quickly considered as a thing-of-the-past:

  • Developer mindset: .NET OSS and also C# will evolve. There will come a point where it’ll feel pretty awkward for .NET programmers working with 4.8 to not be able to use all the new goodies. Could you imagine programming with C#3 nowadays?
  • Third-Party Libraries: The .NET 4.8 / .NET OSS increasing gap will push open-sourced libraries authors toward .NET OSS. The cost of maintaining two code bases will be too high for an OSS developer. If your .NET 4.8 application consumes some OSS libraries, not migrating it will put you in an awkward situation where you’ll have to maintain the OSS code consumed yourself! Certainly serious commercial libraries will be maintained on both platforms for a longer period of time, but not forever.
  • Performance: We can expect more and more performance improvements with .NET OSS.
  • Tooling: Tools will continue to evolve and with time, less and less tool will support .NET 4.8 application.

Recently I’ve discussed with Jean-Baptiste Evain that develops the OSS library Cecil. Jb is also responsible for UnityVS at MS. Here at NDepend we’re relying on Cecil for more than a decade. Cecil processes compiled .NET assemblies bytes and thus will obviously benefit from Span<T> only available on .NET Core. This concrete situation illustrates well the points mentioned above:

  • By using Span<T> Jb is not enthusiast to have to maintain two versions of Cecil, one relying on Span<T> and the .NET 4.8 one.
  • Even though these two versions will co-exist because it is too early to discard the .NET 4.8 version of Cecil used by many serious projects, it is a matter of a few years until .NET 4.8 Cecil version gets deprecated.
  • Without Span<T> the .NET 4.8 version of Cecil will be slower.

Our case

NDepend is still running on .NET 4.8. NDepend is a CI tool, a standalone UI tool, an Azure DevOps extension and a Visual Studio extension. Developing an extension is a sensitive situation because we need to align our platform with the platform of the host. VS is such a massive application that I don’t expect it to run on .NET 5 in 2021. On the other hand VS is evolving so quickly nowadays that this possibility is not totally excluded. It is also possible that Microsoft takes an incremental approach and that the main VS process (devenv.exe) will remain on .NET Fx 4.8 for a while, while children processes run on .NET OSS (VS runs with quite a few children processes!).

At this point the reasonable move for us is to anticipate the migration to .NET OSS mostly by compiling as much code as possible against .NET Standard 2.0, supported by both .NET Fx 4.7.2+ and .NET Core. We also need to make sure that our WPF and Winforms code will be easily movable (which shouldn’t be a problem since most of the WPF/Winforms APIs are supported by .NET 3.1). We are also mulling over on having our own child process(es) but all the UI part must remain in the main VS process.

We also keep in mind that it will be tricky to support the future VS version running on .NET OSS and previous VS versions running on .NET v4.8 for a few years.

Conclusion

Those like us still working on a large .NET 4.8 legacy are entering into a turbulence zone for the years to come. However for all the reasons explained above, we can expect that in not so long (2023? 2025?) successful applications still running on .NET 4.8 will be the exception. Certainly not anticipating legacy migration now is likely a strategic mistake.

4 Predictions for the Future of .NET

In May 2019, Microsoft officially announced .NET 5, the future of .NET: it will be based on all the .NET Core work already achieved. Here is the schedule announced:

On one hand the future of .NET has never been so bright. On the other hand this represents a massive move for all .NET development shops, especially for those that still target .NET Framework 4.x that won’t evolve anymore. But not everything is clear from this announcement. Such massive move will have many collateral consequences that we can only guess by now. Certainly many points are not yet cast in stone and still debated.

Hence for large .NET legacy code bases some predictions must be made to plan now a seamless and in-time migration toward the future of .NET. So let’s do some predictions: it’ll still be interesting to come back in a few years and see how good or bad they were.

.NET Standard won’t evolve much

.NET Standard was introduced as a common API set that all .NET flavors must implement. .NET Standard superseded PCL (Portable Class Library). Now that several .NET frameworks will be unified upon .NET Core bases, and that the .NET Framework 4.x won’t support future versions of .NET Standard anymore, it sounds like the need for more .NET standard API will decrease significantly. Actually .NET Framework 4.8 doesn’t even support latest .NET Standard 2.1: “.NET Framework 4.8 will remain on .NET Standard 2.0 rather than implement .NET Standard 2.1”.

However .NET Standard is certainly not dead yet: it is (and will be for years to come) an essential tool to compile code into portable components that can be reused across several .NET flavors. However with this unification process the future of .NET Standard is compromised.

Visual Studio will run on .NET 5 or 6 (and in a x64 process)

It has to. Imagine the consequences if in 3 years from now (2019 Q4) the main Microsoft IDE for .NET professional developments still run on .NET Framework v4.8:

  • Engineers working on VS would lack access to all new .NET APIs, performance improvements and langage improvements. They would remain locked in the past.
  • As a consequence they wouldn’t use their own tool (dogfooding) and dogfooding is a key aspect of developing tools for developers.
  • Overall the message sent wouldn’t be acceptable for the users.

On the other hand, if you know a bit how VS works, imagine how massive this migration is going to be. For more than a decade there have been a lot of complaints from the community about Visual Studio not running in a 64 bits process. See some discussions on reddit here for example. If I remember well this x64 request was the most voted one when VS feedback was still handled by UserVoices. Some technical explanations have been provided by Microsoft like those ones provided 10 years ago! If in 2019 Visual Studio still doesn’t run in a x64 process, this says a lot on how large and complex such migration is.

It seems inevitable that this time the Visual Studio legacy will evolve toward what will be the future of .NET. One key benefit will be to run in a x64 process and have plenty of memory to work with very large solutions. Another implication is that all Visual Studio extensions, like our extension, must evolve too. Here at NDepend we are already preparing it but it will take time, not because we’ll miss much API (we’ll mostly miss AppDomain) but because:

  • We depend on some third-parties that we’d like to get rid of to have full control over our migration, and overall code.
  • For several years we’ll have to support both future Visual Studio versions and Visual Studio 2019, 2017 and maybe 2015 that runs on .NET Framework v4.x (btw we still support VS 2013/2012/2010 but this will have to be discarded to benefit from .NET Standard reused DLLs)

We cannot know yet if Visual Studio vNext will run on .NET 5 or if it’ll take more years until we see it running upon .NET 6?

Btw here are 2 posts Quickly assess your .NET code compliance with .NET Standard and An in-depth analysis of .NET Core 3.0 support for WPF and Winforms APIs that can help plan your own legacy migration.

.NET will propose a cross-platform UI Framework: WPF or a similar XAML UI Framework

On October 4, 2019 Satya Nadella revealed why Windows may not be the future of Microsoft’s business. In August 2019 Microsoft provided a .NET Cross Platform UI Framework Survey. Clearly a .NET cross-platform UI Framework is wanted: the community is asking for it. So far Microsoft closed the debate about WPF: WPF won’t be multi-platform.

Let’s also be crystal clear. This (WPF cross platform) is a very hard project. If the cost was low, this would be a very different conversation and very likely a different outcome. We have enough trouble being compatible with OpenSSL and that’s just one library.  Rich Lander – Dec 5, 2018

But given the immense benefits of what WPF running cross-platform would offer, I wouldn’t be surprise to see WPF become cross-platforms within the next years. Or at least a similar XAML UI framework. Moreover WPF is now open-source so who knows…

The Visual Studio UI is mostly based on WPF hence one of the benefit of having WPF cross-platform would be to have a unique cross-platform Visual Studio: the same way Microsoft is now unifying .NET Frameworks, they could unify the Visual Studio suite into a single cross-platform product.

Xamarin Forms and Avalonia are also natural candidates to be the .NET cross-platform UI Framework. But it seems those frameworks doesn’t receive enough love from the community, this is my subjective feeling. Also we have to keep in mind that Microsoft did a survey and that the community is massively asking for it.

Blazor is promised to a bright future

If you didn’t follow the recent Blazor evolution, the promises of this technology are huge:

  • Run .NET code in all browsers (like Silverlight)
  • with no browser plugin needed (unlike Silverlight)
  • with near-native performance
  • with components compiled to a compact binary format

This is all possible thanks to the WebAssembly (Wasm) format supported by most browsers.

WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable target for compilation of high-level languages like C/C++/Rust, enabling deployment on the web for client and server applications.

Blazor was initially a personal project created by Steve Sanderson from Microsoft. It was first introduced during NDC OSLO in July 2017: the video is worth being watched, also read how enthusiastics are the comments. However Blazor is not yet finalized and still has some limitations: it doesn’t offer yet a decent debugging experience and the application size to download (a few MBs) is still too large because dependencies have to be loaded too. Those ones are currently being addressed (see here for debugging and here for download size, runtime code will be trimmed and cached and usage of CDN (Content Distribution Network) is mentioned).

The community is enthusiast, the technology is getting mature and there is no technological nor political barrier in sight: the Blazor future looks bright. Don’t miss the Blazor FAQ to learn more.

.NET Core 3.0 New APIs

.NET Core 3.0 has just been released, see here the official announcement. In this post we’re going to explain how to list and explore the new APIs introduced since .NET Core 2.2 (this API diff is also available here).

To diff the API versions download NDepend trial, start VisualNDepend.exe, and click Compare 2 versions of a code base.

  • In the Older Build add assemblies in the folder: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\2.2.5
  • In the Newer Build add assemblies in the folder: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\3.0.0

Then click OK. Depending on your hardware it’ll take between a dozen of seconds and a minute to analyze these two versions of .NET Core.

Here are the lists of new APIs obtained:

To obtain these lists we’ve edited 5 code queries and exported their results to HTML. For example to obtain the 28 new namespaces we edited the query:

And got this result, that also lists new types for each new namespace:

.NET Core 3.0 New Namespaces

The 4 others code queries used to list new types, new types and their members and new methods and new fields introduced in existing types are:




Find API Breaking Changes in your .NET Libraries and Frameworks

If you are developing a framework, the last thing you want to happen when releasing a new version of your product is to break the code of your clients because of an API breaking change. For example, you want to make sure that all public methods you had in previous versions are here in the next version, unless you tagged some of them with System.ObsoleteAttribute for a while before deprecation.

Let’s underline that we are talking here of syntactic breaking changes that can be all detected with tooling as we are about to see. On the other hand semantic breaking changes are code behavior changes that will break your client code. Those breaking changes cannot be found by a tool, but are typically caught by a solid test suite.

One key feature of NDepend is to be able to compare all results against a baseline snapshot. Code querying can explore changes since the baseline. For example this code query detects methods of an API that are not anymore visible from clients:

This query code is pretty simple and readable but let’s detail it a bit:

  • The warnif count > 0 prefix transforms this code query into a code rule
  • We iterate on methods in the baseline thanks to codeBase.OlderVersion().Application.Methods
  • We want methods that were publicly visible in the baseline and not just public. Being declared as public is not enough for a method to be publicly visible. A public method can still be declared in a class declared as internal.
  • We don’t warn for public methods that were deemed as obsolete in the baseline
  • We handle both situations: A) publicly visible methods removed since the baseline and B) publicly visible methods not publicly visible anymore.

This query is pretty similar than the source of the default rule API Breaking Changes: Methods that is a bit more sophisticated to handle more situations, like when the public method return type changes. This rule also presents results in a polished way.

Similar rules are available for publicly visible types and publicly visible fields.

Some other sorts of breaking changes are detected when for example, a publicly visible interface or base class changes: in such situation clients code that implement such interface or derive from such abstract class will be broken.

Some rules also detect when a serializable types gets broken and when an enumeration Flags status change.

Real Experiments

From comparing NHibernate v5.2.x against NHibernate v4.1.x here are breaking changes found: 36 types, 995 methods, 205 fields (including enumeration values) and 121 interfaces or abstract classes have been changed. I guess most of those breaking changes are about elements that were not intended to be seen by API consumers in the baseline version but a detailed analysis would be needed here.

NHibernate v5.2 vs v4.1 Breaking Changes

I’ve also compared .NET Core v2.2.5 with .NET Core v3.0 Preview 8 but hopefully didn’t find any breaking change. However NDepend has an heuristic code query to find types moved from one namespace or assembly to another and here more than 200 types were matched like the class ArrayList moved from the assembly System.Collections.NonGeneric.dll to the assembly System.Private.CoreLib.dll. Hopefully such changes are invisible to the clients consuming .NET Core as a NuGet package.

ArrayList moved from System.Collections.NonGeneric.dll to System.Private.CoreLib.dll

Find new APIs consumed and APIs not used anymore

A related and interesting topic is to review new APIs used by an application or APIs not used anymore. This is possible from the NDepend > Search Elements by Changes panel with the buttons Third Party Code Elements – Used Recently / Not Used Anymore. For example the screenshot below shows new API consumed by the application eShopOnWeb:

New APIs used by eShopOnWeb

Before releasing a new version, this is interesting to have a glance at API consumption changes. Doing so often sheds light on interesting points.

Business Complexity vs. Implementation Complexity

It is good software design practice to make sure that methods can be entirely viewed in the code editor that typically shows 30 to 45 lines at a time. The root of this principle is easy to grasp: scrolling up and down over a too large method impedes code readability.

Refactoring a too large method or a too large class implies to create several code elements smaller in terms of number of textual lines. But ultimately the code behavior didn’t change. In other words the business complexity remained the same but the refactor session reduced the implementation complexity (at least we hope so).

Software complexity is a subjective measure relative to the human cognition capabilities. Something is complex when it requires effort to be understood by a human. Software complexity is a 2 dimensional measure. To understand a piece of code one must understand both:

  • What this piece of code is doing at runtime, the behavior of the code, this is the business complexity.
  • How the actual implementation solves the business needs at runtime, this is the implementation complexity.

The whole point of software design, SOLID principles, code maintainability, patterns… is to avoid adding implementation complexity to the business complexity. But from where implementation complexity comes from? There are 5 main sources of implementation complexity:

Too Large Code

We already mentioned this one. It is easy to limit implementation complexity by abiding by simple code rules that check thresholds on code metrics like methods number of lines of code and method complexity, or classes with too many methods. The Single Responsibility Principle (SRP) also contribute to smaller implementations: less responsibilities for a class necessarily means less code.

Lack of Abstractions

An abstraction such as an abstract method, an interface or even a delegate, is the minimum required knowledged to invoke an implementation at runtime without knowing it at design time. In other words an abstraction hides implementation detail. Abstraction represents a great weapon to reduce the implementation complexity because polymorphism can replace quantity of if/else/switch/case. Concretely code like that:

Can be replaced with code like that:

Abstraction also reduces implementation complexity because it frees the developer mind of implementation details.

The S in SOLID is the SRP (mentioned above) and is not related to abstraction. But the OLID principles are all about using abstractions wisely:

How do we check for good usage of abstractions? There is no magic stick like thresholds to limit too large code elements. However the Abstractness vs. Instability graph and metrics and the Level metric are a good start to identify code areas that need more abstractions. They are both described in this post about Dependency Inversion Principle.

State Mutability at Runtime

A common source of implementation complexity is mutable states. The human brain is not properly wired to anticipate what is really happening at runtime in a program. While reviewing a class, it is hard to imagine how many instances will simultaneously exists at runtime and how the states of each these instances will evolve over time. This is actually THE major source of problems when dealing with a multi-threaded program.

If a class is immutable (if the states of all its instances objects don’t change at runtime once the constructor is called) its runtime behavior immediately becomes much easier to grasp and as a bonus, one doesn’t need to synchronize access to immutable objects. See here a post explaining in-depth immutable class.

A method is a pure function if it produces outputs from its inputs without modifying any state. Like immutable classes, pure functions are also a good mean to reduce implementation complexity.

Some code rules exists to enforce state mutability like Structure should be Immutable or Fields should be marked as ReadOnly when possible. Being immutable for a class or pure for a method is such an essential property that dedicated C# keywords for that would be welcome, like readonly for fields. Here is a proposal for C# support of the immutable keyword. By now some ImmutableAttribute or PureAttribute can be used to tag such elements and some code rule can check for Types tagged with ImmutableAttribute must be immutable or Methods tagged with PureAttribute must be pure

Over Coupling

When trying to re-engineer/understand/refactor a large piece of code (I mean something made of dozens of classes like a big namespaces or an assembly), the difficulty is directly proportional to the amount of coupling between considered code elements. Both dependency graphs below shows dependencies between 36 classes: but the left contains 64 edges and the right one contains 133 edges: which one would you prefer to deal with?

One key strategy to control coupling is to layer components and make sure that there is no dependency cycles. This can be checked on namespaces for example with the code rule Avoid namespaces dependency cycles. Using abstractions is also a good way to limit over coupling. If an interface has N implementations then relying only on one interface is virtually like depending on the N underlying classes.

Lack of Unit Tests

Software testing is a large topic I won’t cover here. But one key benefit of writing tests (apart enforcing business rules and detecting regressions early) is to ensure that the code is testable. Being testable for code means less coupling, more abstractions and overall simple code. Ultimately if the code is easily testable we can safely assume that its implementation complexity is kept low. Here also many code rules like Code should be tested can help enforce high testability.

One Measure for All

There are more sources of implementation complexity but those 5 ones are certainly the bigger culprits. To reduce this unnecessary complexity one must be able to measure it. But how to unify too large code, bad design, poorly tested code and more in a single metric?

As we saw most of these aspects can be enforced with code rules. A code rule produces issues and for each issue the code rule can estimate the cost to fix an issue and the annual cost to let an issue unfixed. A famous analogy with the financial field says that:

  • The estimated cost to fix code smells is the Technical-Debt: a measure of the implementation complexity.
  • The estimated annual cost to let code smells unfixed is the Annual Interest of the Debt: a measure of the business operation cost induced by poorly written and poorly tested code.

These estimations can be expressed in developer-time and ultimately in money cost which can be used by management to take the right decisions.

Answers to arguments against 100% coverage

I’ve been enthusiast about 100% coverage for more than a decade. The large code base of NDepend we are working on will reach soon 90% overall coverage. Most classes tested are being 100% covered.

In the heatmap below small rectangles are methods. Grapes of rectangles are classes namespaces and assemblies. By importing code coverage in this heat-map we can see at a glance that most classes are green: they are 100% covered. This heatmap also shows in red and orange areas with room for improvements. Areas in gray represent untestable code tagged with System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute.

Not everybody agrees with 100% coverage and the same points against used to popup again and again so I take a chance to address them one by one.

I don’t waste my time writing tests to cover trivial code

Often the example of trivial code proposed is property getter and setter. Indeed writing tests to cover such get/set methods doesn’t bring any value. But such trivial code is meant to be used by real code, isn’t it?

For example suppose we have a Contact class with a property like Contact.FirstName, this properties is meant to be used. The module that builds an invoice and that prints the contact name on it certainly uses it. And if the build invoice module is well tested, Contact.FirstName must get called implicitly at test time.

So if some trivial code is not covered by tests, the idea is not to write dumb tests to cover it, the idea is to question yourself why the trivial code is not already implicitly covered by tests that exercise real code. And certainly you’ll find room for improvements in the tests suite in charge of testing the real code, that depends upon the trivial uncovered code. A future post will go through benefit of 100% coverage but as this point suggests, one key benefit is that a hole in 100% covered code is always an indicator of something interesting to improve.

90% coverage is enough

Often teams get satisfied with 90% or 95% coverage. For the scale of a large code base such score is outstanding. But for the scale of a class or a component 100% must be the goal.

The key is testability. We all have experienced some easy to test code and some hard to test code. The maintainability is hard to measure but the testability is something concrete. And good testability leads to good maintainability. In other words if a component can be fully tested seamlessly it means this component will be easy to maintain.

On the other hand, in the real world we often end up with a component that contains a small portion that is hard to test. And we get satisfied with 90 or 95% coverage. But this is sweeping dust under the carpet: this small portion of code is not testable because it is not well designed and as a consequence it is bug-prone. Hence we end up not testing the most sensitive part of the code that likely concentrates most of the problems!

If after having wrote a test suite you end up with a few if/else blocks that are hard to test, do a favor to yourself: refactor to reach seamless full testability.

What matters is what gets asserted in tests, not the amount of coverage

Suppose you have a 1.000 lines of code linear method: it only contains a linear list of processing with no if/then/switch/case… Suppose you have a test with zero assertions invoking this method. You end up with 1.000 lines of code 100% covered but not tested at all.

This fictitious situation often proposed as 100% coverage counter argument doesn’t reflect the reality. In the real world if one works hard to get a complex class 100% covered, I cannot imagine that the tests don’t contain an healthy amount of assertions. No matter if TDD Test First Design approach is used or if tests get written at the same time as code tested : writing tests leads to think more and to think better. One doesn’t write a test without having a few points in mind to assert.

But there is more. Why only tests should contain assertions? The code itself is well suited to contain assertions. For more than a decade we stuff the NDepend code base with assertions. Everything that can be asserted gets asserted. We end up with more than 26K assertions. A good half of those comes from non-nullable references. C#8 nullable reference will relieve all those assertions. But will remain all other assertions about non-empty strings, special string formats, non-zero counter, IDictionary.ContainsKey(), non-zero denominator of a division, !object.IsDisposed, loop invariant…. And all those assertions are checked at test time and fail tests when violated. Ultimately most tests end up with a 1/10 ratio between the number of assertions in test checked, and the number of assertions in code checked.

We still rely on the good old System.Diagnostics.Debug.Assert() but this is an implementation detail. Any assertion library can be used from the code itself (including a custom one, or the Code.Contracts library that would have deserved more love). The only thing that matters is to tune assertions to fail a test upon violation, typically by sending an exception. A related topic is if those assertions must be checked or not at production time. This is another interesting debate with pros and cons.

Some code just cannot be tested

Indeed there are some API call that just cannot be tested like calls to System.Windows.MessageBox. Such calls need to be mocked. This way the code is well splitted between 100% covered code and code tagged with System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute. Dependency Injection can be used to inject untestable implementation in tested code.

Again testability is a central characteristic of code. Partitioning code according to its testability makes sense.

However, there are situations where such untestable API tend to prolifer. Then your choice is to either mock everything or accept to live with non-tested code. But being in this situation is the sign that such API is immature because not test-prone. Thus something radical must be done about that: get rid of it, contribute to it (if OSS), fork it…

UI Code is untestable

Since testing code has become a popular practice, UI code has always been treated as an awkward case: there are some frameworks dedicated to UI testing but such practice remains tedious and increases test maintenance. This is not satisfying.

As for any code, UI code must be written with testability in mind. UI code can contain UI logic but should not contain business logic. For example the UI class shouldn’t contain the implementation of a IsContactReadOnly() method (business logic) but can call such method declared in a non-UI testable class, to decide if a Contact First Name textbox should be readonly or editable (UI logic). Ideally UI code looks like an empty shell as much as possible.

Nowadays web development outperforms desktop developments. The backend spits HTML CSS and Javascript textual content. This content seen as data represents some easily testable material. However when possible, it is preferable to test the IsContactReadOnly() business logic directly from the backend code, than to test it indirectly, for example by checking if an HTML textbox is readonly or editable. Hopefully modern web platforms like ASP.NET Core propose testing strategies.

Desktop UI is a different beast to test and there is no magic. To test our UI specific code we’ve invested in an architecture where the UI code can be piloted, both from the main-form code and from tests. Such architecture relies massively on some mediator classes: any UI portion can pilot any other UI portion through these mediators. These mediators are used at production runtime, and tests supersede them to pilot the UI at testime. Tests dedicated to UI testing have few assertions, but such tests are not useless: they execute all the assertions stuffed in our UI code. At the end of the day some of our UI classes remain not fully covered but closed to. Small portions left untested doesn’t contain error-prone code but untestable UI peculiarities, like complex designer code or some DPI related code that can only be tested manually. The experience proved that this approach saved us from regression bugs many times.


100% coverage is a sensitive topic. In the NDepend team we are 100% coverage driven. In a future post we’ll detail the benefits of being 100% coverage driven. But I wanted to detail first the non-trivial reasons that make 100% coverage worth in most situations.

 

Static Analysis and Dependency Injection

For quite some years now, we (the NDepend team) got some demand about resolving Dependency Injection, see this page on our User Voices. Lately we’ve been considering such support carefully but came to the conclusion that it’d be awkward. On the other hand we have alway been prioritizing features development based on user feedback and need. So our conclusions are not definitive yet. I write this post to not only expose our point of view, but eventually trigger more productive discussions.

Static Analysis

First let’s underline that NDepend is a static analyzer. It gathers 100% of its data from the code itself, and also parses more data like code coverage. Static analysis is in opposition with dynamic analysis that gathers data from runtime execution.

  • Static analysis deals with classes, methods and design measures like lines of code or complexity. It is design-time analysis.
  • Dynamic analysis deals with objects and runtime measures like performance or memory consumption. It is run-time analysis

Dependency Injection (DI)

Recently I explained the basis of Dependency Injection (DI) and how it relates to DIP in the post SOLID Design: The Dependency Inversion Principle (DIP). DI is all about depending on interfaces instead of depending on classes. Here is simple DI sample code, ClientCode() depends on IDbConnection and not on SqlConnection. The power of DI is that now ClientCode() can run on other RDMS than SQL Server, like Oracle or SQLLite. ClientCode() can now also be unit-tested seamlessly by mocking the interface IDbConnection.

Stable Dependency Principle (SDP)

DI is an application of the Stable Dependency Principle (SDP), which is not part of the SOLID principles, but is much related with the Dependency Inversion Principle (DIP).

SDP states: The dependency should be in the direction of the stability. Stability is a measure of the likeliness of change. The more likely it will change, the less it is stable.

The Liskov Substitution Principle (LSP) and the Interface Segregation Principle (ISP) articles explain how interfaces must be carefully thought out. As a result an interface is a stable code element, that is less likely to change than the the classes that implement it. Following the SDP, it is better design to rely on interfaces than to rely on concrete classes.

The Users need: supporting DI through Static Analysis

In this context, users want NDepend to be able to parse DI code like that.

Concretely users want NDepend to bind all calls to an IDbConnection member with a call to the corresponding SqlConnection member . For example a dependency toward SqlConnection.Open() should be created for any method calling IDbConnection.Open().

But why would this be useful? Here is a user feedback:

We have layered architecture: Foundation, Feature, Project. Following current NDepend report, everything follows right dependencies. Project depends on Feature and Foundation. Feature depends on Foundation. However due to some of DI registrations, we have complex dependencies (…) there is dependency from Foundation to Project in reality. Interface is declared in Foundation, but implementation is in Project and registered via DI. It means that Foundation projects, that are core and stable part of solution, depend on Project level, which is not so stable and reliable.

In reality is highlighted in bold, because the user really meant at run-time. At run-time Foundation depends on Project, the same way that at run-time ClientCode() depends on SqlConnection. But at design-time Foundation doesn’t depend on Project, the same way that at design-time ClientCode() doesn’t depends on SqlConnection. At design-time ClientCode() only knows about IDbConnection.

From this, one implies that not only at design time, but also at runtime dependencies should go from Project towards Foundation. In other words, one feels that implementing an adapter into Project and injecting it into a Foundation component “is not so stable and reliable”, but that’s not how those principles are meant and described.

As a matter of fact, being able to inject Project adapters into Foundation components (where the Project adapter implements one of Foundation’s interfaces) is the core of the Dependency Inversion Principle (DIP). Without it, it is impossible to create flexible systems. This can be unreliable, but only if one doesn’t follow the Liskov Substitution Principle (LSP), when Foundation interfaces are not well thought-out, like for example when ICollection<T> forces the Array class to implement ICollection<T>.Add() which obviously cannot be supported.

Conclusion

Hence it looks like this feature need results from a confusion between design-time dependencies and run-time dependencies. The purpose of design is to reduce the cost of change, anything else is not design. Managing design-time dependencies is at the heart of design. All SOLID principles and all principles surrounding SOLID like the Stable Dependency Principle (SDP) are about design-time dependencies.

Moreover adding DI support would be a never-ending story—and I guess it cannot be done right without information collected at run-time. DI Containers are very complex beasts internally, their behavior often changes from one major version to the next, and there are dozens of DI Containers in .NET (Autofac, UnityContainer, NInject, StructureMap, MEF, SimpleInjector, Castle.Windsor, LightInject, Prism, Spring.net … just to name a few).

Again all our developments are driven by users-need, but on this particular need we’re not sure it’d make sense. We let the discussion open on this post and on the User Voices page. Whether you agree or disagree with our positions, feel free to comment.