Start with ModelsBuilder and actually use it
I’ll just say it: years ago, I often used .Value("propertyAlias") because, hey, it was quick. Deadlines were tight, proof-of-concept code needed to ship, and strongly typed models sometimes felt like an extra luxury. Typing out an alias is quick, right?
But those little shortcuts have a funny way of sticking around way past the sprint review. What started as a hacky way to get things working ends up making it into production. Then six months later someone renames a property, content goes missing, your logs spout exceptions, and you find yourself hunting through dozens of files trying to find every string alias in use.
If you’ve worked with Umbraco for a while, you’ve run into this at least once.
This is where ModelsBuilder just wins. Suddenly you’re turning runtime headaches into compile-time feedback. Your IDE flags problems immediately—no more hoping you’ll spot a broken alias later.
Take a look at this:
@Model.Value<string>("pageTitle")
Sure, it works fine. No error, the code runs. But you’re relying on a magic string. You don’t get IntelliSense, you don’t get validation, and if someone changes that alias, you won’t know until something breaks.
Now compare that to:
@Model.PageTitle
Clean, readable, and so much easier to maintain. Visual Studio (or Rider, if that’s your thing) actually helps you find your way around. Refactoring is less of a nail-biter. New team members can follow along without picking through the document types.
And the bigger your project, the more important this becomes. With a pile of document types and hundreds of properties, strongly typed models stop being nice-to-have—they’re essential. I don’t even think of ModelsBuilder as an optimization anymore. It’s just how I work now.
Let Dependency Injection do the heavy lifting
One of the best things about modern Umbraco is how it blends naturally into the ASP.NET Core world. If you remember the old days—static helpers everywhere, strange service locators, composers sprinkled around—you know how wild the ecosystem used to be. More than one way to do everything, and not all of them aged gracefully.
Now, with Umbraco 17, things just align way better with .NET conventions. And that’s a big win.
Developers familiar with ASP.NET Core don’t have to unlearn habits to start an Umbraco project. Concepts like dependency injection, configuration, logging, and service registration just work the way you’d expect.
Below, a standard service:
public class BlogService {
private readonly IPublishedContentQuery _contentQuery;
public BlogService(IPublishedContentQuery contentQuery) {
_contentQuery = contentQuery;
}
public IEnumerable<IPublishedContent> GetLatestPosts() {
return _contentQuery.ContentAtRoot()
.DescendantsOfType("blogPost")
.OrderByDescending(x => x.CreateDate)
.Take(5);
}
}
Not exactly groundbreaking, right? But that’s the point. Good architecture should be boring—it should be clear where dependencies come from and what they’re doing. If you stick with these conventions, everyone spends less time struggling with infrastructure, and more time actually building features.
And that, believe it or not, is one of the fastest ways to boost productivity.
Stop rebuilding components
Every Umbraco dev hits this scenario: the marketing folks want a hero banner. So you build one. Next, they ask for a version with a background image. Soon it’s a hero with a video, or one with right-aligned content, or a fancy hero just for campaign pages. Before you know it, you’ve coded six slightly different heroes that all do pretty much the same thing.
It feels productive at first—you’re getting things done. The reality? You’re piling up maintenance headaches. Bugs need fixing in six places, design updates mean hunting down every hero variant, and onboarding new devs gets harder with every extra component you add.
I’m all in on spending a bit more time up front to build flexible, reusable components. With Umbraco 17’s Block Grid editor, this is smoother than ever. Instead of crafting a new solution for every page, you build blocks that editors can mix and match, all while keeping things manageable for developers.
Your first shot at this might take a bit longer, but the next twenty pages go up way faster. That’s a trade I’ll take every time.
Break up large views before they become a problem
Nothing screams technical debt like opening a Razor file and hitting the scroll wheel immediately. We’ve all taken over projects with monster templates—hundreds of lines, conditionals inside more conditionals, chunks of code that nobody dares to touch anymore.
Large views are a drag. They’re a pain to read and slow everything down. Every change takes extra steps, merge conflicts pop up everywhere, bugs take twice as long to find.
Instead, I break things into partial views or view components, and I do it early.
Something like:
@await Html.PartialAsync("Components/Hero", Model)
@await Html.PartialAsync("Components/FeatureGrid", Model)
@await Html.PartialAsync("Components/LatestArticles", Model)
Might look like more work at first glance, but it leads to a cleaner, more organized codebase. Developers can zero in on the right spot, components get reused, testing is easier, and the repo’s history stays readable.
More important than all that: the project stays manageable as it grows.
Get comfortable with the Content Delivery API
A few years ago “headless CMS” sounded pretty niche. Now? More and more businesses want their content everywhere: not just on the website, but in apps, portals, displays, custom search experiences—sometimes channels they haven’t even thought of yet.
That’s where the Content Delivery API comes in. Even if your current project uses traditional MVC, learning the API right now gives you a serious advantage for the future. You can expose content like this:
GET /umbraco/delivery/api/v2/content/item/home
And it’s ready for anything—apps, integrations, whatever comes next.
Funny thing is, a lot of developers tell themselves “I’ll learn the Content Delivery API when we go headless.” In my experience, the ones who dive in early end up building much smarter content models, because they’re already thinking about multi-channel delivery.
That’s going to matter more and more as digital platforms keep evolving.
Block Grid is more powerful than you think
I have to be honest; when Block Grid first landed, I wasn’t sold. Just looked like another editor I’d have to justify to clients. But my view changed. Block Grid hits that sweet spot between flexibility and control. Editors can create their own pages without dragging developers into every tweak. Meanwhile, devs keep enough structure to avoid chaos.
The real payoff comes later: instead of spinning up a boatload of document types and templates for every landing page, you start assembling sites from a collection of blocks. Adding a new page becomes mostly a matter of configuration, not development.
Editors win. Developers win too (because building near-identical templates is as dull as it sounds).
Examine is probably more capable than you realise
Search always starts out simple: “We just want a search box.” Then they ask for filtering. Then sorting, then relevance tweaking, then autocomplete. Before long, you’re thinking about custom solutions.
Honestly, Examine usually does a lot more than folks realize. You can start with something as basic as:
var results = examineManager
.TryGetIndex("ExternalIndex", out var index)
? index.Searcher.CreateQuery("content")
.Field("nodeName", "umbraco")
.Execute()
: null;
And build from there—filters, scoring, all that stuff.
The real advantage? You spend way less time “reinventing search.” Now, I always start by asking: “How much of this does Examine already handle?” Most of the time the answer is “More than you think.”
Invest in local development speed
This tip isn’t glamorous, but it’s a game-changer: optimize your local dev environment. Developers waste an astonishing amount of time waiting. For builds. For the app to start. For a container. For packages to restore.
Individually those waits—maybe ten seconds here or twenty there—don’t feel like much, but over a year, they pile up to hours or days.
So whenever I join a project, I check this stuff right away:
Can we get the solution up quickly?
Are we loading (or building) projects nobody needs?
Lingering packages nobody uses?
Anything we can automate in the setup?
Can debugging be simpler?
Sprint demos rarely spotlight improvements here, but they pay off massively in the long run. The fastest code is the stuff you don’t have to wait for.
Document the "Weird Stuff"
Every project has “that feature.” The one nobody understands. The integration that works only because someone basically reverse engineered an API over a frantic three days. That weird scheduled task that only works if you deploy things just right. That bespoke workflow everyone tiptoes around.
Once you finally figure these out, write them down—right away, not “when you get time.” The best documentation always happens while the challenge is still fresh in your head. Even a quick markdown file explaining why something’s built the way it is can save someone (probably you) a ton of time in the future.
Six months from now, you’ll be very glad you documented it when you did.
Wrapping Up
When people talk about developer productivity, it’s all “try this tool, learn this new framework, keep up with the trends.” But the biggest wins usually just mean removing friction.
Strongly typed models prevent mystery errors. Dependency injection keeps your architecture clear and easy to follow. Reusable components cut your maintenance headaches. Fast, smooth local environments give you back whole days or weeks of your life.
None of these ideas are revolutionary—and that’s exactly why they work.
Umbraco 17 feels more and more like standard .NET. That means less time fighting the CMS, more time actually solving problems. In the end, that’s the heart of productivity: not churning out more code or more features, but building better solutions with less pain, less wasted time, and creating a codebase your future self will thank you for.