Hey kids! Let’s chat about NFR’s (Non Functional Requirements). So, what exactly is that??
These requirements refer to the things you should add to your code that are additional to the functionality.
An example would be if you have a website that consumes an API, you need that http request and response for your site to function. The things you also should have in your website would be logging or health checks.
These things do not make the website run. But they are essential to make sure shout site is running as expected.
If you don’t add error handling and logging to your site, how will you know when you get errors in your code? You could re-test your site manually every day, but I would not recommend that 😂😂 So, how do we know if our code errors?
we add Try Catch blocks, with the error logging inside the Catch section. Depending on your preferred method of notification, you may insert a log row to a database, or send an email, or push a message to an API endpoint that routes the error to the appropriate notification type.
Below, find a list of the most common NFR’s and how to easily implement these additions into your code.
Error handling / Logging
When you log errors, you do everyone a favor around you, including yourself. When something goes wrong in your code (yes I said WHEN), if you don't have logging and/or your logging is generic, it will take you forever to find the bug and fix it. The rule of thumb is if an error could occur here and you need to know what variables held values, add them to the error logged. Here's an example:
catch (Exception ex)
{
LoggerService.LogToSystem("The variable a = " + a + ". The method to review is MethodName. The system error captured is " + ex.Message)"
return false;
}
Obviously, the user does not need to see this full error, so you log this error and show the user a human readable error based on whatever is happening.
try
{
CODE
return true;
}
catch (Exception ex)
{
LoggerService.LogToSystem(ex.Message, ex, request,
Logging.LogLevel.Error, HttpContext.Current.User);
return false;
}
finally
{
//do clean up
request.close():
}
Please do this. Okie dokie?
Security
Security will be something your company will require in a specific manner. You may have security team that works on firewalls, ports, network settings (like TLS versions, etc). If you do not have these folks and you are the security team - please review minimum security requirements from Microsoft or another eputable company.
Some built-in security for .NET Core projects created in Visual Studio would be authorization and authentication protocols. Please make sure you get familiar with how these work and how to keep your program safe.
Health Check
Add the HealthChecks nuget package and include the following pieces in your startup.cs
using Microsoft.Extensions.Diagnostics.HealthChecks;
public void ConfigureServices(IServiceCollection services)
{
services.AddHealthChecks();
}
public void Configure()
{
endpoints.MapHealthChecks("/healthz", new HealthCheckOptions
{
ResponseWriter = WriteResponse
});
}
Design Patterns
It is always good to stick with similar design patterns in each project. Maybe the Repository pattern works best for your application, where another may work best with the Factory pattern. Almost always, you will use the Singleton pattern for things like the logged in user. You only want one global instance of a user.
NOTE: I've linked to a few different free learning sites so you can review additional patterns. There are tons of them that are extremely useful.
Unit / Integration Tests
My current company uses Unit test and Integration tests much more than any other company I've worked for. Learning how to write code 'Test first' is a great way to really learn unit testing and understand it. I'm planning to write a post about unit tests more in depth later.
Unit tests can consist of multiple types of test frameworks. I've used NUnit, XUnit and Microsoft Fakes the most in my career. Each has it own pros and cons.
Make sure you import and use at least one testing option. If you want to use NUnit, add the nuget package.
The add your using statements to your test project in your solution. Yes, the unit tests need to be separate.
using Microsoft.QualityTools.Testing.Fakes;
using NSubstitute;
using NUnit.Framework;
Then write your tests based on what you are actually testing. Don't forget the Test decorator or the test will not run.
[Test]
public void ServiceIsNull_ReturnsNullException()
{
//Arrange
var testService = Substitute.For<IService>();
var loggerService = Substitute.For<ILoggerService>();
//Act
//Assert
var exception = Assert.Throws<ArgumentNullException>(() => new Service(loggerService, null, testService ));
Assert.AreEqual("Service", exception?.ParamName);
}
Integration testing is also important. This type of testing groups testing, instead of testing an individual module or function, you are testing the larger system pieces, but not the entire system.
Generally you will use the [SetUp] and [TearDown] decorators to setup any external objects needed to be subbed, created, or faked. This is where you would bind necessary artifacts that you need to do overall testing for your service or project. Detailed information on integration testing is here.
Feature flags
Feature flags can be annoying but they serve a hugely wonderful purpose. When you push new code behind a feature flag that is off, then Production doesn't usually do a massive crash and burn. You can then turn on the flag at your leisure and monitor your changes until you're confident the additional code was successful. You can use your own 'homegrown' flagging system or utilize a third party option like LaunchDarkly.
Whatever you choose to use, make sure you do lots of testing when you first add it so you know the flags will behave as expected once in production.
If (FeatureFlag_AddNewCode)
Then {
NEW CODE
}
EXISTING CODE HERE
If (FeatureFlag_AddNewNewCode)
Then {
NEW NEW CODE
}
The feature flag then toggles if the code is on or off. Each new code change should have its own separate flag to toggle only the single piece of code it is meant to run. Once your code is running successfully after monitoring, then you can go back in and remove old feature flags.
I hope this post has offered some insight into the base requirements you should have in every solution, regardless of the code being written. Happy coding!
Comments