Your Todo application is too complex or not complex enough
I've blogged before about ASP.NET Architect David Fowler's hidden gems in ASP.NET. His GitHub is worth following because he's always exploring and thinking and he's doing it in public. I love reading other people's source code.
He's been working on a local orchestrator called Micronetes that is worth reading about, but for this blog post I want to focus on his "Todos" repository.
Making a Todo List is a form of Hello World on the web, similar to making a blog or a simple website. Everyone knows what a Todo app should look and act like, so you can just focus on your tools and not on the requirements. You may feel that a Todo app "isn't complex enough" or isn't a good example app to make. That's fine, but it is worth exploring and reading the different ways the same thing can be done.
David's repository https://github.com/davidfowl/Todos is of note because it's not ONE Todo App. As of the time of this writing it's 8 todo apps, each with a different reason to exist.
What's a basic app look like? What if you add Auth? What if you add Dependency Injection? What about Controllers? You get the idea.
Some languages and platforms (*ahem* enterprise) get a reputation for being too complex, too layered, too many projects. Others may get the opposite reputation - that's a toy, it'll never scale (in size, traffic, size of team, whatever).
The point is that not everything is a hammer and not everything is a screw. You may think this is a cop out, but the answers is always "It depends." The more experience you get in software and the more mistakes you make and the more systems you put into production the more you'll realize that - wait for it - it depends. Disagree if you like, but one size doesn't fit all.
Some cool stuff about David's Todo code
All that said, there's some cool "before and afters" if you look at the code for earlier ideomatic C# and what newer APIs and language features allow. For example, if we assume some extensions and new APIs added for clarity, here's a POST
static async Task PostAsync(HttpContext context)
{
var todo = await context.Request.ReadJsonAsync<Todo>(_options);
using var db = new TodoDbContext();
await db.Todos.AddAsync(todo);
await db.SaveChangesAsync();
context.Response.StatusCode = StatusCodes.Status204NoContent;
}
and the GET
static async Task GetAllAsync(HttpContext context)
{
using var db = new TodoDbContext();
var todos = await db.Todos.ToListAsync();
await context.Response.WriteJsonAsync(todos, _options);
}
I personally do think that stuff like this is too complex. I hate that out parameter.
static async Task GetAsync(HttpContext context)
{
if (!context.Request.RouteValues.TryGet("id", out long id))
{
context.Response.StatusCode = StatusCodes.Status400BadRequest;
return;
}
using var db = new TodoDbContext();
var todo = await db.Todos.FindAsync(id);
if (todo == null)
{
context.Response.StatusCode = StatusCodes.Status404NotFound;
return;
}
await context.Response.WriteJsonAsync(todo);
}
This is made-up code from me that doesn't work. It's even still a little too much.
static async Task GetAsync(HttpContext context)
{
if (!RouteValues.Exist("id")) return Http.400;
using var db = new TodoDbContext();
var todo = await db.Todos.FindAsync(RouteValues["id"] as int);
if (todo == null) return Http.404
await Json(todo);
}
These are all useful exercises and are fun to explore. It also brings up some hard questions:
- What is the difference between terse and clear versus obscure and inaccessible?
- How important is the Law of Demeter?
- Are some problems better solved by language changes or by main library changes?
- How many things should/can be put into extension methods?
- And when those basic scenarios break down, are you dropped into a Func<T<T<T<T<T<T>>>>> hellscape?
Do you enjoy reading code like this as much as I do, Dear Reader? I think it's a great learning tool. I could do a whole day-long class facilitating conversation around this code https://github.com/davidfowl/Todos
Enjoy!
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
Comments are closed.
Yes but also don't forget that when it comes to coding languages, often times "terse" can become "obscure".
I'd tilt for more verbose but understandable. Not advocating, like, a million lines per class or anything, just pointing out that it's possible (and easily done) to obfuscate the meaning of a piece of code in the interests of being brief.
It's not necessarily better to have the minimum lines of code to get a given job done.