Programming Time Capsule

These days, while watching YouTube, I found a Mexican YouTuber explaining what life was like in his city during the 2020 COVID-19 pandemic. I thought it was a good idea to write something similar but for coding. This is an opinionated view of what coding is like in 2020. Software developers from the future, this is programming in 2020.

GitHub has archived all public repositories until July 2020. It was part of his archive initiative. We earned a batch on our GitHub profiles if any of our repositories got into the Arctic vault. I got mine too. It will show future generations how code was in 2020. Should we be ashamed or proud? I don’t know. But this archive doesn’t show some of the practices around it.

Dear developers from the future, this is coding and interviewing in 2020.

1. On coding

  • Visual Studio 2019 is the latest version of Visual Studio. C# 8 is the latest C# version. And we don’t have SQL Server Management Studio for Mac or Linux yet.
  • Visual Studio Code is the most popular IDE. This is a different one.
  • Windows is still the most used operating system among developers.
  • JavaScript is the most popular programming language. The market is divided between React, Angular, and Vue. Although, every once in a while, a new front-end framework appears. Or a new version appears, changing almost everything from all previous versions. We even have a term for that: JavaScript fatigue.
  • Single-page applications are the norm now. Especially when you build them with one of the trending frameworks. Or with a library built on top of one of them.
  • Everyone is doing microservices these days. Monoliths are the evildoers.
  • Machine Learning and Artificial Intelligence are the next big things.
  • Everybody, when stocked, uses StackOverflow. It’s a place to post questions and receive answers. A forum.
  • Null is still a problem in mainstream languages.
  • When things break, we say “It works on my machine”. Then, we came up with containers. So we can ship developer’s machines to the clients or the cloud.
  • Most developers upload and share their code on GitHub. Oh yes! Git is the most popular version control system. There are also GitLab and BitBucket.
  • Every day, we have a (hopefully) short meeting. A “daily meeting.” It’s part of a ceremony called SCRUM methodology. Everyone calls himself “Agile” to hide the fact companies don’t know what they’re doing.
  • I’m writing this from a laptop with a 1.8GHz 4-Core processor, 16GB of memory, 500GB of hard drive, and a 6-hour battery life.

2. On interviewing

  • Interviewing is broken. Everybody with a blog complains about the interview process. Even on Twitter. Oh, Twitter. The place to complain in less than 280 characters.
  • We solve or attempt to solve algorithms and data structures exercises on whiteboards. Although, a study reveals whiteboarding only tests the candidate’s ability to deal with stress. Most of the time, we don’t use those subjects after the interview process.
  • There are pages to train for whiteboarding: HackerRank, LeetCode, CodeWars
  • Interviewing is based on rejection. Only a small percentage of applicants are hired. A story tells some managers at a big company rejected all applications they were asked to review. Later, the secret was revealed. They reviewed their own applications.
  • Ninjas, superheroes, wizards, 10x engineers…Ping-pong tables, open spaces, cool offices, being “agile” and [put the latest next big thing here] are often listed on job descriptions as perks and benefits.

During the 2020 global pandemic, some companies turned remote. We started to use Zoom, a conference room tool. Most people started working from home without any previous experience working remotely. _“Please, turn off your microphone.” “You’re muted.” We all heard these phrases in meetings every once in a while.

I hope the 2020 pandemic is still on Wikipedia or whatever you have these days to look things up…or are brains already connected to the Internet, like in the Matrix movie? Do you watch Matrix in class? Wait! Do you still have schools?

Greetings from 2020

How I started blogging and why you should start too

Once upon a time, a junior engineer at his first job

Everything started with my first professional job.

I was a junior software engineer, the least experienced on the team. I had just finished reading The Clean Code. And I wanted to rewrite all the code I had worked with.

I had a lot to share with my colleagues about coding. But I was the new guy. So I came up with “Daily Tips.” It was a weekly email with a single tip about writing better code.

Those tips came from what I had seen in the book and in the code I worked with. For example, use boolean variables instead of integers for flags. I started to accumulate some of these tips on my personal computer.

A few years later, those tips ended up in my presentations for newcomers at my next job.

The first post

The real starting point was a few years later in my second job.

After getting tired of writing log statements to chase down bugs, I went to the Internet to see what was out there. There must be a better way!–I thought.

I found Fody, a solution using Aspect-Oriented Programming. I put up a proof of concept and showed it to my team lead. Unfortunately, we ended up doing something else. But I had my findings. And I didn’t want to lose that time and that how-to behind an unread email. My blog and its first post were born.

Next posts

After that, from time to time, I started to share my learning and my experiences.

I started to write about the bugs that literally gave me headaches, the resources I used to learn languages and frameworks, and the notes from the books I read. And here you are, 30 posts after that first post.

Your turn

You don’t need to wait to be a well-known figure in the tech field to have a blog.

A blog is a means to share your learning. To learn in public. To share your insights. To show your work. That will make your blog unique.

I don’t have anything to write about?–you said. Have you learned something new? Share that. Share the resources you used to learn it.

Probably, next time you’re Googling something, you will find your own blog posts.

Thanks for reading!

Ultralearning: Takeaways

Scott Young describes in Ultralearning the strategy behind his own learning challenges, like “MIT Challenge in 1 year” and “A year without English.” Let’s learn what Ultralearning is all about. These are my takeaways.

Ultralearning is a self-directed and intense strategy to learn any subject. Ultralearning projects help to advance careers or excel at a particular subject. Ultralearning is a perfect alternative to traditional learning methods.

1. Before starting

Before starting an ultralearning project, answer why, what, and how you are going to ultralearn.

Why?

First, identify why you’re learning a subject and the effect you want to achieve. Are you learning the subject to get a specific result? Are you driven only by curiosity?

For example, are you learning to code to get a promotion? Or do you want to learn a new language to go on a trip? Those are two different motivations.

What?

Next, determine the concepts, facts, and procedures you need to learn.

  • A concept is something you need to understand instead of memorizing
  • A fact is something you need to memorize. Facts are useful only if you can recall them later
  • A procedure is something you need to practice

For example, learning vocabulary and expressions are facts when learning a foreign language. But pronunciation is procedural.

How?

After answering Why and What, select your resources. Spend about 10% of your learning time doing research. Use this research time to find how people are learning that subject.

Look for syllabi of courses, textbooks, boot camps, and experts in that field. Filter what won’t help you to achieve your goal.

Library collection
Find how people are learning your study subject. Photo by Christian Wiediger on Unsplash

2. During

Learn in context

Learning should be in the context where those skills will be applied. It’s doing the thing you want to get good at where most of the learning happens. For example, solve problem sets instead of watching lectures and learn a language through conversations instead of vocabulary lists.

Try project-based learning and immerse learning. For example, learn how to create a website in a month or go on a trip to learn a new language.

Prefer short study sessions

Spread shorter study sessions over a long period. Find a balance between long study sessions on a single topic and shorter sessions on different subjects. It’s better to have shorter sessions, between 15 minutes and an hour.

If you find yourself procrastinating, follow the 5-minute rule: start and sustain for 5 minutes. Also, you can try the Pomodoro technique: spread 25-minute practice sessions between 5-minute breaks.

Time Timer Watch
Photo by Ralph Hutter on Unsplash

Identify bottlenecks

Identify the bottleneck components in your learning. Separate your skill into sub-skills. And practice each sub-skill. Imagine a musician who practices tricky parts of a piece in isolation and then practices everything.

Recall instead of concept mapping

Recalling is better than concept maps and passive note reviewing. Recall concepts and facts. Your memory is a leaky bucket. Try space-repetition software or flashcards. After watching a lecture, write all you can remember. When practicing, avoid using your resources.

Voilà! Those are my takeaways from the Ultralearning book. It changed how I approach learning. Instead of overloading my brain with information, I start by creating a plan and list of learning resources.

For more learning content, check my takeaways from Pragmatic Thinking and Learning, one of my favorite books on the subject, and my advice on starting an Ultralearning project to become a Software Engineer.

Happy ultralearning!

Let's Go: Learn Go in 30 days

Do you want to learn a new programming language but don’t know what language to choose? Have you heard about Go? Well, let’s learn Go in 30 days!

From its official page, Go is “an open source programming language that makes it easy to build simple, reliable, and efficient software”.

Go is a popular language. According to Stack Overflow Developer Survey, since 2020, Go is in the top 10 of most admired/desired languages and in the top 15 of the most popular languages.

Docker, Kubernetes, and a growing list of projects use Go.

Why to choose Go?

Go reduces the complexity of writing concurrent software.

Go uses the concept of channels and goroutines. These two constructs allow us to have a “queue” and “two threads” to write to and read from it, out-of-the-box.

In other languages, we would need error-prone code to achieve similar results. Threads, locks, semaphores, etc, …

Rob Pike, one of the creators of Go, explains channels and goroutines in his talk Concurrency is not parallelism.

How to learn Go? Methodology

To learn a new programming language, library or framework, stop passively reading tutorials and copy-pasting code you find online.

Instead, follow these two principles:

1. Learn something by doing. This is one of the takeaways from the book Pragmatic Thinking and Learning. Instead of watching videos or skimming books, recreate examples and build mini-projects.

2. Don’t Copy and Paste. Instead of copy-pasting, read the sample code, “cover” it and reproduce it without looking at it. If you get stuck, search online instead of going back to the sample. For exercises, read the instructions and try to solve them by yourself. Then, check your solution.

“Instead of dissecting a frog, build one”.

― Andy Hunt, Pragmatic Thinking and Learning

Resources

Before starting to build something with Go, we can have a general overview of the language with the Pluralsight course Go Big Picture.

To grasp the main concepts, we can follow Learn Go with tests. It teaches Go using the concept of Test-Driven Development (TDD). Red, green, and refactor.

Other helpful resources to are Go by Example and Go documentation.

“To me, legacy code is simply code without tests.”

― Michael C. Feathers, Working Effectively with Legacy Code

Basic

Intermediate

Advanced

Fullstack

You can find more project ideas here: 40 project ideas for software engineers, What to code, Build your own x and Project-based learning.

Conferences

Conclusion

Go was designed to reduce the clutter and complexity of other languages. Go syntax is like C. Go is like C on asteroids. Goodbye, C pointers! Go doesn’t include common features in other languages like inheritance or exceptions. Yes, Go doesn’t have exceptions.

However, Go is batteries-included. You have a testing and benchmarking library, a formatter, and a race-condition detector. Coming from C#, you can still miss assertions like the ones from NUnit or XUnit.

Aren’t you curious about a language without exceptions? Happy Go time!

You can find my own 30-day journey following the resources from this post in LetsGo

canro91/LetsGo - GitHub

How to add an in-memory and a Redis-powered cache layer with ASP.NET Core

Let’s say we have a SlowService that calls a microservice and we need to speed it up. Let’s see how to add a caching layer to a service using ASP.NET Core 6.0.

A cache is a storage layer used to speed up future requests. Reading from a cache is faster than computing data or retrieving it from an external source on every request. ASP.NET Core has built-in abstractions for a caching layer using memory and Redis.

1. In-Memory cache

Let’s start with an ASP.NET Core 6.0 API project with a controller that uses our SlowService class.

First, let’s install the Microsoft.Extensions.Caching.Memory NuGet package. Then, let’s register the in-memory cache using the AddMemoryCache() method.

In our Program.cs file, let’s do this,

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();

builder.Services.AddTransient<ISlowService, SlowService>();

builder.Services.AddMemoryCache(options =>
//               ^^^^^
{
    options.SizeLimit = 1_024;
    //      ^^^^^
});

var app = builder.Build();
app.MapControllers();
app.Run();

Since memory isn’t infinite, we need to limit the number of items stored in the cache. Let’s use SizeLimit. It sets the number of “slots” or “places” the cache can hold. Also, we need to tell how many “places” a cache entry takes when stored. More on that later!

Decorate a service to add caching

Next, let’s use the decorator pattern to add caching to the existing SlowService without modifying it.

To do that, let’s create a new CachedSlowService. It should inherit from the same interface as SlowService. That’s the trick!

The CachedSlowService needs a constructor receiving IMemoryCache and ISlowService. This last parameter will hold a reference to the existing SlowService.

Then, inside the decorator, we will call the existing service if we don’t have a cached value.

public class CachedSlowService : ISlowService
{
    private readonly IMemoryCache _cache;
    private readonly ISlowService _slowService;

    public CachedSlowService(IMemoryCache cache, ISlowService SlowService)
    //     ^^^^^
    {
        _cache = cache;
        _slowService = slowService;
    }

    public async Task<Something> DoSomethingSlowlyAsync(int someId)
    {
        var key = $"{nameof(someId)}:{someId}";
        return await _cache.GetOrSetValueAsync(
        //                  ^^^^^
            key,
            () => _slowService.DoSomethingSlowlyAsync(someId));
    }
}

Set Size, Limits, and Expiration Time

Let’s always use expiration times when caching items.

Let’s choose between sliding and absolute expiration times:

  • SlidingExpiration resets the expiration time every time an entry is used before it expires.
  • AbsoluteExpirationRelativeToNow expires an entry after a fixed time, no matter how many times it’s been used.
  • If we use both, the entry expires when the first of the two times expire
A child playing colorful videogames
If parents used SlidingExpiration, kids would never stop watching Netflix or using smartphones! Photo by Sigmund on Unsplash

Let’s always add a size to each cache entry. This Size tells how many “places” from SizeLimit an entry takes.

When the SizeLimit value is reached, the cache won’t store new entries until some expire.

Now that we know about expiring entries, let’s create the GetOrSetValueAsync() extension method. It checks first if a key is in the cache. Otherwise, it uses a factory method to compute and store a value into the cache. This method receives a custom MemoryCacheEntryOptions to overwrite the default values.

public static class MemoryCacheExtensions
{
    // Make sure to adjust these values to suit your own defaults...
    public static readonly MemoryCacheEntryOptions DefaultMemoryCacheEntryOptions
        = new MemoryCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(60),
            // ^^^^^
            SlidingExpiration = TimeSpan.FromSeconds(10),
            // ^^^^^
            Size = 1
            // ^^^^^
        };

    public static async Task<TObject> GetOrSetValueAsync<TObject>(
        this IMemoryCache cache,
        string key,
        Func<Task<TObject>> factory,
        MemoryCacheEntryOptions options = null)
            where TObject : class
    {
        if (cache.TryGetValue(key, out object value))
        {
            return value as TObject;
        }

        var result = await factory();

        options ??= DefaultMemoryCacheEntryOptions;
        cache.Set(key, result, options);

        return result;
    }
}

Register a decorated service

To start using the new CachedSlowService, let’s register it into the dependency container.

Let’s register the existing SlowService and the new decorated service,

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();

// Before:
//builder.Services.AddTransient<ISlowService, SlowService>();

// After:
builder.Services.AddTransient<SlowService>();
//               ^^^^^
builder.Services.AddTransient<ISlowService>(provider =>
//               ^^^^^
{
    var cache = provider.GetRequiredService<IMemoryCache>();
    var slowService = provider.GetRequiredService<SlowService>();
    return new CachedSlowService(cache, SlowService);
    //         ^^^^^
});

builder.Services.AddMemoryCache(options =>
{
    options.SizeLimit = 1_024;
});

var app = builder.Build();
app.MapControllers();
app.Run();

As an alternative, we can use Scrutor, an “assembly scanning and decoration” library, to register our decorators.

Let’s use the Remove() method to delete cached entries if needed. We don’t want to use outdated or deleted values read from our cache by mistake.

There are only two hard things in Computer Science: cache invalidation and naming things.

– Phil Karlton

From TwoHardThings

Unit Test a decorated service

Let’s see how to test our decorator.

We need a fake for our decorator and assert it’s called only once after two consecutive calls. Let’s use Moq to create fakes.

[TestClass]
public class CachedSlowServiceTests
{
    [TestMethod]
    public async Task DoSomethingSlowlyAsync_ByDefault_UsesCachedValues()
    {
        var cacheOptions = Options.Create(new MemoryCacheOptions());
        var memoryCache = new MemoryCache(cacheOptions);
        //                ^^^^^
        var fakeSlowService = new Mock<ISlowService>();
        fakeSlowService
            .Setup(t => t.DoSomethingSlowlyAsync(It.IsAny<int>()))
            .ReturnsAsync(new Something());
        var service = new CachedSlowService(memoryCache, fakeSlowService.Object);
        //            ^^^^^

        var someId = 1;
        await service.DoSomethingSlowlyAsync(someId);
        await service.DoSomethingSlowlyAsync(someId);
        //            ^^^^^
        // Yeap! Twice!
        
        fakeSlowService.Verify(t => t.DoSomethingSlowlyAsync(someId), Times.Once);
        // Yeap! Times.Once!
    }
}

Now, let’s move to the distribute cache.

2. Distributed cache with Redis

A distributed cache layer lives in a separate server. We aren’t limited to the memory of our application server.

A distributed cache is helpful when we share our cache server among many applications or our application runs behind a load balancer.

Redis and ASP.NET Core

Redis is “an open source (BSD licensed), in-memory data structure store, used as a database, cache, and message broker.” ASP.NET Core supports distributed caching with Redis.

Using a distributed cache with Redis is like using the in-memory implementation. We need the Microsoft.Extensions.Caching.StackExchangeRedis NuGet package and the AddStackExchangeRedisCache() method.

Now our CachedSlowService should depend on IDistributedCache instead of IMemoryCache.

Also we need a Redis connection string and an optional InstanceName. With an InstanceName, we group cache entries with a prefix.

Let’s register a distributed cache with Redis like this,

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();

builder.Services.AddTransient<SlowService>();
builder.Services.AddTransient<ISlowService>(provider =>
{
    var cache = provider.GetRequiredService<IDistributedCache>();
    //                                      ^^^^^
    var slowService = provider.GetRequiredService<SlowService>();
    return new CachedSlowService(cache, SlowService);
    //         ^^^^^
});

builder.Services.AddStackExchangeRedisCache(options =>
//               ^^^^^
{ 
    options.Configuration = "localhost";
    //      ^^^^^
    // I know, I know! We should put it in an appsettings.json
    // file instead.
    
    var assemblyName = Assembly.GetExecutingAssembly().GetName();
    options.InstanceName = assemblyName.Name;
    //      ^^^^^
});

var app = builder.Build();
app.MapControllers();
app.Run();

It’s a good idea to read our Redis connection string from a configuration file instead of hardcoding one.

In previous versions of ASP.NET Core, we also had the Microsoft.Extensions.Caching.Redis NuGet package. It’s deprecated. It uses an older version of the StackExchange.Redis client.

Redecorate a service

Let’s change our CachedSlowService to use IDistributedCache instead of IMemoryCache,

public class CachedSlowService : ISlowService
{
    private readonly IDistributedCache _cache;
    private readonly ISlowService _slowService;

    public CachedSlowService(IDistributedCache cache, ISlowService slowService)
    //                       ^^^^^
    {
        _cache = cache;
        _slowService = slowService;
    }

    public async Task<Something> DoSomethingSlowlyAsync(int someId)
    {
        var key = $"{nameof(someId)}:{someId}";
        return await _cache.GetOrSetValueAsync(
            key,
            () => _slowService.DoSomethingSlowlyAsync(someId));
    }
}

Now let’s create a new GetOrSetValueAsync() extension method to use IDistributedCache instead.

This time, we need the GetStringAsync() and SetStringAsync() methods. Also, we need a serializer to cache objects. Let’s use Newtonsoft.Json.

public static class DistributedCacheExtensions
{
    public static readonly DistributedCacheEntryOptions DefaultDistributedCacheEntryOptions
        = new DistributedCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(60),
            // ^^^^^
            SlidingExpiration = TimeSpan.FromSeconds(10),
            // ^^^^^
            
            // We don't need Size here anymore...
        };

    public static async Task<TObject> GetOrSetValueAsync<TObject>(
        this IDistributedCache cache,
        string key,
        Func<Task<TObject>> factory,
        DistributedCacheEntryOptions options = null)
            where TObject : class
    {
        var result = await cache.GetValueAsync<TObject>(key);
        if (result != null)
        {
            return result;
        }

        result = await factory();
        await cache.SetValueAsync(key, result, options);

        return result;
    }

    private static async Task<TObject> GetValueAsync<TObject>(
        this IDistributedCache cache,
        string key)
            where TObject : class
    {
        var data = await cache.GetStringAsync(key);
        if (data == null)
        {
            return default;
        }

        return JsonConvert.DeserializeObject<TObject>(data);
    }

    private static async Task SetValueAsync<TObject>(
        this IDistributedCache cache,
        string key,
        TObject value,
        DistributedCacheEntryOptions options = null)
            where TObject : class
    {
        var data = JsonConvert.SerializeObject(value);

        await cache.SetStringAsync(key, data, options ?? DefaultDistributedCacheEntryOptions);
    }
}

With IDistributedCache, we don’t need sizes in the DistributedCacheEntryOptions when caching entries.

Unit Test a decorated service

For unit testing, let’s use MemoryDistributedCache, an in-memory implementation of IDistributedCache. This way, we don’t need a Redis server in our unit tests.

Let’s replace the MemoryCache dependency with the MemoryDistributedCache like this,

var cacheOptions = Options.Create(new MemoryDistributedCacheOptions());
var memoryCache = new MemoryDistributedCache(cacheOptions);         

With this change, our unit test now looks like this,

[TestClass]
public class CachedSlowServiceTests
{
    [TestMethod]
    public async Task DoSomethingSlowlyAsync_ByDefault_UsesCachedValues()
    {
        var cacheOptions = Options.Create(new MemoryDistributedCacheOptions());
        var memoryCache = new MemoryDistributedCache(cacheOptions);
        //                ^^^^^
        // This time, we're using an in-memory implementation
        // of IDistributedCache
        var fakeSlowService = new Mock<ISlowService>();
        fakeSlowService
            .Setup(t => t.DoSomethingSlowlyAsync(It.IsAny<int>()))
            .ReturnsAsync(new Something());
        var service = new CachedSlowService(memoryCache, fakeSlowService.Object);
        //            ^^^^^

        var someId = 1;
        await service.DoSomethingSlowlyAsync(someId);
        await service.DoSomethingSlowlyAsync(someId);
        // Yeap! Twice again!

        fakeSlowService.Verify(t => t.DoSomethingSlowlyAsync(someId), Times.Once);
    }
}

We don’t need that many changes to migrate from the in-memory to the Redis implementation.

Conclusion

Voilà! That’s how we cache the results of a slow service using an in-memory and a distributed cache with ASP.NET Core 6.0. Additionally, we can turn on or off the caching layer with a toggle in our appsettings.json file to create a decorated or raw service.

For more ASP.NET Core content, read how to compress responses and how to serialize dictionary keys. To read more about unit testing, check my Unit Testing 101 guide where I share what I’ve learned about unit testing all these years.

Happy caching time!