C++ has a special kind of technical debt: header include hell. It starts innocently enough. A few includes here, a convenience header there, one “temporary” dependency that nobody cleans up later. Then the project grows. Suddenly: - include order starts to matter - circular dependencies creep in - weird compiler errors appear far away from the real cause - developers start over-including files “just in case” - build times slow to a crawl - even IntelliSense begins to lose its mind And in template-heavy codebases, it gets even worse. Because templates live in headers, every bad dependency decision gets amplified across the entire project. The real fix is not another workaround. It’s discipline. - Treat includes as a tree, not a graph. - Keep headers small and focused. - Split types, functions, classes, and data structures into logical units. - Group code by purpose, not by convenience. And when a header starts becoming a junk drawer, refactor it without hesitation. Yes, forward declarations can help. But too often they’re used as painkillers instead of a cure. If your code constantly needs them just to stay compilable, there’s a good chance your dependency structure is already sick. Bad include hygiene is not a minor inconvenience. It is architecture debt with compound interest. A clean include structure doesn’t just improve compile times. It improves readability, maintainability, tooling, and the team’s ability to change the code without fear. In C++, headers are not just files. They are a map of your design quality. If your include graph looks like spaghetti, your architecture probably does too. #cpp #cplusplus #softwarearchitecture #technicaldebt #buildsystems #programming #gamedev #softwareengineering #cleanCode
Valentyn Ivanov’s Post
More Relevant Posts
-
🔷 Abstract class vs Interface in modern C# — when does it actually matter? This is one of those questions that comes up in every code review, yet the answer is rarely nuanced enough. Here's my breakdown 👇 ───────────────────────────── ✅ Choose an Abstract Class when: → You have shared logic to reuse across subclasses (concrete methods, fields, constructors) → You need to maintain shared state — interfaces can't hold backing fields → You want to enforce constructor chaining with base(args) → You're modeling a true "is-a" relationship (Dog IS-A Animal) ───────────────────────────── ✅ Choose an Interface when: → You need multiple contracts — C# has no multiple inheritance, but you can implement many interfaces → You're defining a capability, not an identity (IDisposable, ISerializable, ICloneable) → You want maximum testability — interfaces are far easier to mock → You're building a public API for external implementors ───────────────────────────── 💬 Rule of thumb I always come back to: • Shared code/state → Abstract class • Capability across unrelated types → Interface • Constructor enforcement → Abstract class • Multiple "contracts" → Interface • Public API for external implementors → Interface ───────────────────────────── Which pattern do you reach for first? Drop it in the comments 👇 #csharp #dotnet #softwareengineering #cleancode #programming
To view or add a comment, sign in
-
Hello, C++ developers. [Post 13] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ TOPIC: Copy Elision and Return Value Optimization (RVO) Master compiler optimizations that eliminate copy/move operations entirely by constructing objects directly in their final destination. What this carousel covers: → Copy elision principle - construct → copy → destroy becomes just construct in place → RVO (Return Value Optimization) - returning prvalues, mandatory in C++17 → NRVO (Named RVO) - returning named locals, optional but common → C++17 guaranteed copy elision - prvalue expressions bypass copy/move entirely → std::move prevents NRVO - never use std::move on return statements → Multiple return paths - usually prevent NRVO optimization → Non-copyable types - work with RVO (C++17+), not with NRVO unless compiler applies it → Automatic move fallback - if NRVO fails, compiler uses move instead of copy → Real-world measurements - construct only vs construct + move comparisons Key insights: ◆ C++17 RVO is mandatory for prvalues - return Object() always elides, no copy/move possible ◆ NRVO is optional - return local_var may or may not elide, compiler-dependent ◆ std::move on return prevents RVO/NRVO - forces move when elision would have occurred ◆ Return by value is efficient - trust the compiler, don't use out-parameters for optimization ◆ Multiple return paths block NRVO - compiler can't determine single object address ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Note: LinkedIn compresses carousel images. For best experience, download the PDF or pinch-zoom on mobile. #CPlusPlus #CPP #RVO #NRVO #CopyElision #CompilerOptimization #CPP17 #Programming #SoftwareEngineering #PerformanceOptimization #ZeroCopy #ModernCPP #TechEducation #DeveloperLife #CodeOptimization #Prvalue #ReturnValueOptimization #TechnicalExcellence #AdvancedCPP #CompilerMagic
To view or add a comment, sign in
-
Most developers think LINQ is just cleaner syntax. It’s not. LINQ is an execution pipeline — and if you don’t understand how it works, your elegant one-liners can silently turn into performance bottlenecks in production. 💬 Let’s be honest: What’s the worst LINQ misuse you’ve seen in real systems? 📩 I break down System Design, Backend Performance, .NET internals, and real-world engineering patterns: 🔔 Subscribe: https://lnkd.in/gi3YVNBg 🌐 Deep dives: https://lnkd.in/eJE6DWrq #dotnet #csharp #linq #softwareengineering #backenddevelopment #systemdesign #performance #cleancode #programming #developers #unpopularopinion
To view or add a comment, sign in
-
💡 𝗖#/.𝐍𝐄𝐓 𝐏𝐞𝐫𝐟𝐨𝐫𝐦𝐚𝐧𝐜𝐞 𝗧𝗶𝗽 🔥 💎 𝗣𝗿𝗲𝗳𝗲𝗿 𝗔𝘀𝗦𝗽𝗮𝗻 𝗼𝘃𝗲𝗿 𝗦𝘂𝗯𝘀𝘁𝗿𝗶𝗻𝗴 𝘁𝗼 𝗠𝗮𝘅𝗶𝗺𝗶𝘇𝗲 𝗣𝗲𝗿𝗳𝗼𝗿𝗺𝗮𝗻𝗰𝗲 🐌 𝗧𝗵𝗲 𝗣𝗿𝗼𝗯𝗹𝗲𝗺 𝘄𝗶𝘁𝗵 𝗦𝘂𝗯𝘀𝘁𝗿𝗶𝗻𝗴 Substring creates a new string object and copies characters from the original string. This impacts performance and memory usage, especially with large strings or frequent operations. Every call means new allocations on the heap. 🔥 𝗪𝗵𝘆 𝗔𝘀𝗦𝗽𝗮𝗻 𝗶𝘀 𝗕𝗲𝘁𝘁𝗲𝗿 AsSpan returns a ReadOnlySpan<char>, a lightweight stack-allocated view over the original data. No new objects created, no character copying, just a reference to the existing memory. This leads to significantly better performance and reduced memory overhead. ✅ 𝗪𝗵𝗲𝗻 𝘁𝗼 𝗨𝘀𝗲 𝗘𝗮𝗰𝗵 ◾ Use AsSpan when working with APIs that accept .ReadOnlySpan<char> and you don't need a persistent copy. ◾ Use Substring when you need an actual string copy that outlives the original. ◾ Many modern .NET APIs now support span overloads for better performance #csharp #dotnet #programming #softwareengineering #softwaredevelopment
To view or add a comment, sign in
-
-
💡 C# Tip: Global Usings — Stop Repeating the Same using Statements In many C# projects, the same 5–10 namespaces appear at the top of nearly every file. using System; using System.Collections.Generic; using System.Linq; It works, but it creates boilerplate and visual noise. Since C# 10 (.NET 6+), we have a cleaner solution: 👉 Global Usings 📌 What Are Global Usings? Global usings allow you to declare a namespace once and make it available across the entire project. Instead of repeating imports in every file, you write them one time. Example: global using System; global using System.Collections.Generic; global using System.Linq; The compiler treats them as if they exist at the top of every file in the project. 📂 How to Use Them Create a dedicated file, commonly named: 📄 GlobalUsings.cs Add your global imports there: global using System; global using System.Linq; global using System.Collections.Generic; global using MyCompany.MyProject.Domain; Now those namespaces are automatically available everywhere. ⚡ Why This Matters In projects with 50+ files, global usings: ✔ Reduce repetitive boilerplate ✔ Make files cleaner and easier to read ✔ Centralize common dependencies ✔ Improve consistency across the codebase ⚠ The Trap to Avoid Do not turn global usings into a dumping ground. ❌ Adding everything globally makes dependencies unclear. Best practice: 🔹 Use global usings only for very common namespaces 🔹 Keep rare namespaces local 🔹 Maintain a single GlobalUsings.cs file for discoverability 📌 Best Practices ✨ Use global using Namespace; for shared namespaces 📂 Keep them in a dedicated file like GlobalUsings.cs 📊 Apply only to namespaces used in most files 🧩 Local using statements can still be used when needed 💡 Final Thought Global usings are a small feature with a big readability impact in large .NET projects. Cleaner files → easier navigation → better maintainability. #CSharp #DotNet #ASPNetCore #CleanCode #SoftwareEngineering #BackendDevelopment #Programming 🚀
To view or add a comment, sign in
-
-
In embedded C++, DI looks simple right up to the moment the object graph starts growing. Then it stops being a pattern discussion and becomes a lifetime problem. People often say, “Just use constructor injection.” And yes, that works. But as the system grows, dependency injection stops being only about how dependencies are passed in. It becomes about who creates them, who owns them, how long they live, and how much wiring the design can absorb. I am talking mainly about embedded-style C++ codebases. Not template-heavy compile-time DI, but systems that value explicit object graphs, dynamic polymorphism, predictable behavior, and code that is easy to debug, review, and maintain. In that world, injecting a dependency is rarely just “pass it into the constructor.” You are also deciding whether the dependency is owned or borrowed, whether unique_ptr stays at the composition root or spreads through the graph, whether you need factories to keep construction testable, and whether shared_ptr appears simply because composition got painful. That is why DI often feels harder in C++ than in languages with framework-managed lifecycles. In Java with Spring, the container creates objects, resolves dependencies, and manages lifecycle. In C++, those decisions stay in your code. They are explicit, local, and impossible to ignore. That is both the strength and the cost. C++ gives us precise control over lifetime. What gets harder is scaling that control as the object graph grows. In small systems, explicit wiring feels clean. In larger ones, it can turn into plumbing. In practice, many embedded C++ codebases end up mixing approaches. Constructor injection for clear dependencies. References for borrowed collaborators. unique_ptr where ownership is real. Factory seams where tests need substitution. And only occasionally a container or custom injector when wiring becomes too large to manage by hand. So for me, DI in embedded C++ is not mainly about patterns. It is about how much lifetime and wiring complexity a design can carry before the composition model starts fighting the codebase. How do you handle this in real projects? Do you keep unique_ptr at the composition root? Inject factories for tests? Build your own injector? Or just accept some plumbing as the price of clarity? #EmbeddedSystems #Cpp #SoftwareEngineering #SoftwareArchitecture #EmbeddedSoftware
To view or add a comment, sign in
-
-
Understanding SOLID Principles Today I learned about SOLID principles, which help in writing clean, maintainable, and scalable code in real-world applications. 🔹 S — Single Responsibility Principle (SRP) 👉 A class should have only one responsibility 🔹 O — Open/Closed Principle (OCP) 👉 Open for extension, closed for modification 🔹 L — Liskov Substitution Principle (LSP) 👉 Child classes should be replaceable with parent classes without breaking code 🔹 I — Interface Segregation Principle (ISP) 👉 Don’t force a class to implement unnecessary methods 🔹 D — Dependency Inversion Principle (DIP) 👉 Depend on abstractions, not concrete implementations 💡 Key Takeaway: Following SOLID principles makes code more flexible, easier to maintain, and better for team collaboration. 📌 Next Focus: Applying SOLID principles in real .NET projects #LearningInPublic #SOLID #CleanCode #DotNet #BackendDeveloper #SoftwareDevelopment #Consistency
To view or add a comment, sign in
-
Most C# codebases treat errors as exceptions. I think that's a design mistake. After a month with F#, and two more months with Rust and Go before that, the "errors-as-values" pattern stuck with me. The syntax forces failure to be explicit at the type level. The compiler won't let you ignore it. In Rust that guarantee is airtight. In C# you get more ceremony and softer guarantees, as nothing stops a junior dev from calling `.Value` without checking, but explicit is still better than invisible. The F# version is the cleanest expression of it. A discriminated union for your error cases, `Result.bind` for chaining, and the railway falls apart the moment any step fails. No exceptions, no hidden control flow. 👇 The C# equivalent is achievable. It's more verbose, but the important thing is that failure becomes part of the contract - visible in the signature, handled at the call site, not buried in a catch block three layers up. I've shipped this in production. The concrete win isn't aesthetics, it's that entire bug classes disappear. Unhandled failure paths that lived silently in exception handlers become compiler-visible gaps in a match. Exceptions still have their place: truly unrecoverable situations, infrastructure failures, things that should crash loudly. But for domain validation, business rules, expected failure modes? Errors belong in the return type. If you're designing systems at scale, implicit failure modes are a liability. Making them explicit is an architectural decision, not a style preference.
To view or add a comment, sign in
-
-
Too many if conditions in your service? It’s not bad coding. It’s bad design. I’ve seen this in real systems (and I’ve written it too 👀): 👉 Different user types 👉 Different behaviors 👉 And suddenly… your service becomes a decision machine if (user instanceof PrimeUser) { ... } if (user instanceof InternationalUser) { ... } if (user instanceof GuestUser) { ... } At first, it feels normal. But slowly, your system starts asking: “Which type of user is this?” That’s where things break. Because now, not every user can be safely used everywhere. And that’s exactly what the Liskov Substitution Principle warns us about. 👉 A subclass should replace its parent 👉 Without breaking the system So what’s the fix? Stop checking types. Start trusting behavior. Instead of handling logic in the service, push it into the user itself. Now: ✔ No if conditions ✔ No type checks ✔ Clean, extensible design And most importantly — any User can replace another User safely. That’s real LSP. — If this made you rethink your design, follow for more real-world backend concepts 🚀 Next: Interface Segregation Principle (ISP) #systemdesign #backenddevelopment #java #cleanarchitecture #solidprinciples #softwareengineering
To view or add a comment, sign in
-
🚀 Understanding Dependency Injection (DI) in C# Dependency Injection is one of the most important design patterns every .NET developer should know. Instead of creating dependencies inside a class, DI allows you to inject them from outside — making your code cleaner, flexible, and easier to manage. 👉 Why use Dependency Injection? ✔️ Promotes loose coupling between components ✔️ Makes unit testing easier with mocking ✔️ Improves code maintainability ✔️ Enhances scalability and flexibility 💡 In modern .NET applications, DI is built-in and widely used with services like: Transient Scoped Singleton Once you start using DI properly, you'll notice your code becoming more modular and easier to extend. 👨💻 Clean architecture starts with better dependency management! #csharp #dotnet #dependencyinjection #softwarearchitecture #programming #coding #deepakmalra
To view or add a comment, sign in
-
More from this author
Explore related topics
- How to Achieve Clean Code Structure
- How to Improve Code Maintainability and Avoid Spaghetti Code
- Improving Code Readability in Large Projects
- Why Software Engineers Prefer Clean Code
- Simple Ways To Improve Code Quality
- Why Use CTEs for Cleaner Code
- How to Refactor Code Thoroughly
- Refactoring Problematic Code for Maintainability
- How to Improve Your Code Review Process
- Why Well-Structured Code Improves Project Scalability
Explore content categories
- Career
- Productivity
- Finance
- Soft Skills & Emotional Intelligence
- Project Management
- Education
- Technology
- Leadership
- Ecommerce
- User Experience
- Recruitment & HR
- Customer Experience
- Real Estate
- Marketing
- Sales
- Retail & Merchandising
- Science
- Supply Chain Management
- Future Of Work
- Consulting
- Writing
- Economics
- Artificial Intelligence
- Employee Experience
- Workplace Trends
- Fundraising
- Networking
- Corporate Social Responsibility
- Negotiation
- Communication
- Engineering
- Hospitality & Tourism
- Business Strategy
- Change Management
- Organizational Culture
- Design
- Innovation
- Event Planning
- Training & Development