Ease of use with the complexity for advanced users

Throughout the last year a lot of things have changed. Unlike most people, I had already worked at home for years before the Pandemic began. What was a major change, is that socially my life was completely upended. I’m a gamer. There’s a group of old co-workers/friends that I would get together with once a week or so and go to conventions with. Once I became a Father that changed a bit but I still made it when I could. Everything moved to being virtual.

Virtual conferences, virtual game nights, virtual birthdays …. The list goes on and on. We are now more closer, but further apart than we really have been in recent history. What enables this? The cloud, high speed internet, and modern software. But we have to understand that there are different levels of users that all want to have the same things: enjoy time with people and the things they love to do.

In the past week I’ve used Zoom, RingCentral, teams, Facebook live, Facebook Messenger, Google Meet, Google hangouts, discord, signal … just to name a few. All of these things must take into account the usefulness that they have. What makes them different? How simple are they to use, but also how complex they are. The more features that exist the more you can differentiate yourself.

Software Development Perspective

What does this really mean from a software development perspective? Start off simple. Create one thing that works well. Add in features but ensure that you always keep things simple enough to update and modify. The more complexity you add in the harder it is to maintain.

Hey look I put all of this call into a single complex line of code that does everything!?! Guess what? I hate you and my junior developer that I use to be hates you too.

var summary = new DBContext(config.GetConnectionString()).MyEntityList.GetListOfStuff(x => x.TopLevel.Where(y=>y.CreateDate > DateTime.Now().AddDays(-30)).Select(t => t.Employees).Where(e => e.Salary > x.Salary.Average()))).ToList();

Why do you want to get the connection string and create the dbcontext when you actually query the information? Create a DBContext and reuse it throughout the request. Also is average salary coming from the database? What if we want to pull from an API that gives us the average salary in X or Y business/city/state/country? Then, we need to re-evaluate this entire statement. When things aren’t exactly clear what they will be we should separate out the items to ensure that we can update and modify them easily.

One major thing I see a lot is having business logic within SQL. The thing I quickly follow this up with is how do you test the logic? In the example above our business might decide that they want to user to be able to select which average salary they want to get. IF we split off the average salary call into it’s own query - we can switch between making an API call vs making a database call to pull from different sources of data.

var avgSalary = GetAverageSalary(inputtedParameters.AvgSalaryParams);
var timeDate = GetStartDate(inputtedParameters.EmployeeStartParams);

var summary = dbContext.MyEntityList.GetListOfStuff(x => x.TopLevel.Where(y=>y.CreateDate > startDate).Select(t => t.Employees).Where(e => e.Salary > avgSalary))).ToList();

We made a couple of more changes to clarify this code as well. We made extra functions to deal with getting the average salary based on some input parameters. We also added in a function to get StartDate. This gives us the ability to pull a date from some other reference time period - the last 30, 60, 90 days or even what the start date of the last quarter than can be pulled from a database or other source.

We are missing a major thing here though and that is asynchronous calls. All of these items can be possibly calling out to a different service or system. We want to ensure that our calls are not blocking and wasting resources while waiting for a response.

var avgSalary = await GetAverageSalary(inputtedParameters.AvgSalaryParams);
var timeDate = await GetStartDate(inputtedParameters.EmployeeStartParams);

var summary = await dbContext.MyEntityList.GetListOfStuff(x => x.TopLevel.Where(y=>y.CreateDate > startDate).Select(t => t.Employees).Where(e => e.Salary > avgSalary))).ToListAsync();

With an asynchronous call we can process other requests coming through and get interrupted when the database query returns. Yet we still have issues with this -> what if we have 10,000 employees all making over the average wage that we pull back with this query? Or 5 million because we are pulling back the entire dataset of all employees in America (or some other nation).

var avgSalary = await GetAverageSalary(inputtedParameters.AvgSalaryParams);
var timeDate = await GetStartDate(inputtedParameters.EmployeeStartParams);

var summary = await dbContext.MyEntityList.GetListOfStuff(x => x.TopLevel.Where(y=>y.CreateDate > startDate).Select(t => t.Employees).Where(e => e.Salary > avgSalary))).Skip(inputtedParameters.Skip).Take(inputtedParameters.PageSize).ToListAsync();

With Skip and Take we limit the amount of data being returned. We would want to ensure that the skip and take values are within valid sizes with some data validation but I’m avoiding that for this. Normally you want to ensure that the data size being returned is not gigantic. Many modern systems use consumption/consumption per minute for billing. Forgetting to limit your data will quickly turn a $100/month bill into $100,000/month.

Complexity of Code and the Simplicity of Blocks of Code

As we add in features we need to ensure that we are structuring our application so that it is easy to read and maintain - but also gives our users the complexity that they need. Complexity comes from 2 sources - internal and external. Our own development team may back themselves into a corner by doing early optimization whereas keep blocks of code simple will allow us to create a more complex system.

As developers, we must gain an understanding of what is being asked in order to ensure that we can deliver a useable project that people will actually use. Most often people will simply want to have a system “just work” for them. If they have to continually configure advanced options every single time the use the system, end users will quickly get sick of the wasted time. Allow users to set up complex systems once and reuse. Also, try to provide complex settings as defaulted values that would commonly be used.

Complexity in the Real World

One of my favorite games use to be Elite Dangerous. It provided the opportunity to be a pilot out in a galaxy modeled from the real Milky Way. During an update or a reinstall, my joystick bindings were lost. The control scheme has about 100+ different options. Since that time I’ve tried to play once or twice but I haven’t cared to put the time back in to reset my control scheme and relearn the extra controls that have been put into place since I stopped playing.

The learning curve for users is also incredibly high. They have reduced this over time by providing docking computers that you can add to ships that take care of launching and landing. This almost allows for a serene experience of simply flying through space rather than a tedious simulation of taking off and landing.