Low-Fat vs High-Fat NuGet Packages: A Developer's Decision Guide
Welcome to 2025! As we kick off the new year with fresh perspectives on software development, it’s time to examine one of the most fundamental decisions we make as .NET developers: when to leverage built-in framework features versus external NuGet packages.
This comprehensive guide stems from a recent Coding Night New Zealand session where we explored the concept of “low-fat” (built-in) versus “high-fat” (external package) implementations across three critical areas: validation, dependency injection, and logging.
The Low-Fat vs High-Fat Analogy
Just like we make conscious choices about our diet, especially after the holiday season, we should be equally mindful about the “nutritional value” of our code dependencies.
- Low-Fat (Built-in): Framework features that come out of the box with .NET
- High-Fat (External): Third-party NuGet packages with additional features and complexity
The analogy isn’t about labeling external packages as “bad” – it’s about making informed decisions based on your application’s actual needs.
Three Key Areas Compared
1. Validation: Built-in vs Fluent Validation
The Low-Fat Approach: IValidatableObject
.NET provides built-in validation through System.ComponentModel.DataAnnotations:
| |
Advantages:
- No additional dependencies
- Part of the .NET framework
- Straightforward implementation
The High-Fat Approach: FluentValidation
Using FluentValidation provides a more expressive API:
| |
Advantages:
- More expressive syntax
- Better separation of concerns
- Rich feature set for complex validation scenarios
Considerations:
- Additional dependency
- FluentValidation licensing has changed (version 8+ requires licensing for commercial use)
2. Dependency Injection: Built-in vs Autofac
The Low-Fat Approach: Microsoft.Extensions.DependencyInjection
| |
Advantages:
- Built into .NET Core/5+
- Zero additional dependencies
- Sufficient for most scenarios
The High-Fat Approach: Autofac
| |
When Autofac Makes Sense:
- Legacy .NET Framework to .NET Core migrations
- Complex modular architectures
- Advanced DI scenarios requiring Autofac-specific features
When Built-in DI Suffices:
- Greenfield .NET projects
- Standard dependency injection needs
- Preference for minimal external dependencies
3. Logging: Built-in vs Serilog
The Low-Fat Approach: ILogger with Azure Monitor
| |
Advantages:
- Built into .NET
- Direct integration with Azure Monitor/Application Insights
- Minimal configuration required
The High-Fat Approach: Serilog
| |
Advantages:
- Rich structured logging
- Extensive sink ecosystem
- Powerful configuration options
- Enhanced formatting and filtering
Trade-offs:
- Additional dependencies (Serilog.Extensions.AspNetCore, various sinks)
- More complex configuration
- Potentially overkill for simple logging needs
Decision-Making Framework
When choosing between low-fat and high-fat approaches, consider these factors:
1. Maintainability
- Built-in: Supported by Microsoft, updates with .NET releases
- Third-party: Requires tracking updates, compatibility checking, potential breaking changes
2. Complexity
- Built-in: Simpler, covers basic use cases
- Third-party: Advanced features for complex scenarios
3. Performance
- Built-in: Optimized for .NET runtime
- Third-party: May introduce minimal overhead
4. Dependencies
- Built-in: Zero additional references
- Third-party: Additional packages to manage (dependency hell risk)
5. Community Support
- Built-in: Microsoft backing
- Third-party: Varies by maintainer and community size
6. Features & Flexibility
- Built-in: Core functionality
- Third-party: Rich feature sets for advanced requirements
7. Cost & Licensing
- Built-in: Free as part of .NET
- Third-party: Check licensing terms (some require commercial licenses)
8. Security
- Built-in: Microsoft security assurance
- Third-party: Security depends on maintainer practices
9. Development Speed
- Built-in: May require more effort for complex functionality
- Third-party: Faster development with feature-rich libraries
10. Long-term Vision
- Built-in: Stable, less likely to be deprecated
- Third-party: Risk of abandonment (verify active maintenance)
Software Development Principles to Guide Decisions
YAGNI (You Ain’t Gonna Need It)
Don’t over-engineer for future requirements that may never materialize. Focus on current, well-defined needs.
Example: Don’t implement complex caching, scaling, or advanced logging if you’re building an MVP for market validation.
KISS (Keep It Simple, Stupid)
Prioritize simplicity over complexity. Choose the simplest solution that meets your requirements.
DRY (Don’t Repeat Yourself)
Favor code reusability and avoid duplication, but don’t sacrifice simplicity for abstract perfection.
Product Mindset: The Build-Ship-Tweak Cycle
Adopting a product mindset helps inform these decisions:
- Think - Identify actual requirements
- Build - Implement only what’s needed (MVP approach)
- Ship - Deliver quickly to reduce cost and risk
- Tweak - Iterate based on real-world feedback
Key insight: Faster shipping reduces costs and product risk. The operational cost curve shows that the longer you take to ship, the higher your costs become before you start generating revenue.
Practical Recommendations
Start with Built-in, Upgrade When Needed
- Begin with low-fat implementations for new projects
- Monitor pain points during development
- Upgrade to third-party libraries when built-in features become limiting
- Document the decision rationale for future reference
Consider Your Context
- Greenfield projects: Favor built-in solutions
- Legacy migrations: External packages might ease transition
- Enterprise requirements: May justify additional complexity
- Startup MVP: Keep it simple with built-in features
Team Considerations
- Team expertise: Leverage existing knowledge
- Maintenance capacity: Consider long-term support implications
- Standardization: Align with organizational practices
Real-World Examples from the Session
The demonstration showed three identical applications implementing the same functionality:
- Validation: Both
IValidatableObjectand FluentValidation successfully validated customer data - Dependency Injection: Both built-in DI and Autofac properly resolved and injected services
- Logging: Both
ILoggerand Serilog successfully logged to console and Azure Application Insights
Key takeaway: Both approaches worked for the basic requirements. The choice depends on your specific needs, team preferences, and architectural constraints.
Conclusion
The “low-fat vs high-fat” analogy serves as a useful mental model for making dependency decisions in .NET applications. There’s no universally right answer – the best choice depends on your specific context, requirements, and constraints.
Remember:
- Question your assumptions and challenge existing practices
- Discuss decisions with your team
- Consider the principles: YAGNI, KISS, and DRY
- Think with a product mindset: build what you need now, not what you might need later
- Keep learning and stay open to different approaches
The goal isn’t to avoid external packages entirely but to make conscious, informed decisions about when the additional complexity is justified by genuine business or technical requirements.
Resources
- 📺 Coding Night NZ YouTube Channel
- 📝 Session code samples (available through the presentation)
- 🎯 .NET Validation Documentation
- 🔧 Built-in Dependency Injection
- 📊 ILogger Documentation
What’s your experience with choosing between built-in and external packages? Share your thoughts and decision-making criteria in the comments below!

Join the conversation! Share your thoughts and connect with other readers.