C#
Use of obsolete or weak cryptographic algorithms
9Developers use outdated or vulnerable encryption algorithms like SHA1 or RIPEMD160, failing to recognize their security limitations. This creates security vulnerabilities that are easily avoidable with modern algorithms.
.NET ecosystem rejection due to accumulated pain
9Experienced .NET developers are actively avoiding new .NET projects and migrating to Go, Python, Node.js, and PostgreSQL on AWS due to years of breaking changes, deprecated technologies, and migration pain. C# is perceived as dying for backend/web development.
Enterprise .NET environments stifle innovation and skill growth
7Large enterprise .NET deployments enforce rigid processes, pre-approved design patterns, locked-down machines, and strict governance. Developers have no agency to fix broken systems or optimize workflows, leading to skill atrophy and mediocrity as the goal.
Excessive database calls in ORM usage
7Developers, particularly juniors using Entity Framework or NHibernate, make multiple separate database calls instead of batching queries. Each database call carries significant time overhead, degrading application performance.
Outdated .NET versions lacking modern features
7Developers maintaining codebases on unsupported or feature-limited .NET versions (Framework 4.8, Core 3.1, .NET 5-6) struggle to adopt modern C# features and syntax improvements. The path to upgrading to LTS versions is unclear and refactoring requires careful planning.
Unsafe virtual member access in constructors
6Developers call overridden methods directly from base class constructors, causing code to execute before proper initialization. This is a common error that leads to runtime failures and unexpected behavior.
Lack of awareness about 'using' keyword for resource disposal
6Many C# developers are unfamiliar with the dual purpose of the 'using' keyword, not recognizing it as a tool for proper object disposal and resource cleanup beyond namespace imports. This leads to resource leaks and improper memory management.
Identical LINQ statements returning inconsistent results
6LINQ abstracts collection manipulation but produces unpredictable results when the underlying data source changes format (in-memory objects vs. databases vs. XML). Developers must understand the underlying objects to write correct LINQ code, undermining the abstraction benefit.
Thread-Safe Dictionary Implementation Complexity
6Manual implementation of thread-safe dictionaries using locks is error-prone and inefficient in multi-threaded scenarios, causing developers to either reinvent the wheel or face concurrency bugs.
Compiler warnings accumulated and ignored
6Developers habitually ignore or hide C# compiler warnings, abandoning the benefits of strict type checking. Hidden warnings accumulate unnoticed, and many warnings indicate real defects that eventually manifest as bugs in production code.
Limited Cross-Platform Support
6Although .NET Core improved cross-platform capabilities, C# was originally Windows-centric, and achieving true cross-platform compatibility (macOS, Linux) without code rewrites remains a significant challenge.
Operator precedence surprises in pattern matching and null-coalescing
6The `??` (null-coalescing) operator and pattern matching operators have unintuitive precedence rules. For example, `x is not 0 or 1` evaluates as `(x is not 0) or 1` rather than the intended `x is not (0 or 1)`, leading to subtle bugs and redundant logic.
Inefficient string concatenation patterns in C#
6Developers frequently use string concatenation in loops instead of StringBuilder, causing new memory allocations for each operation. This creates unnecessary performance degradation, especially with repeated append operations on strings.
C# language bloat and inconsistency
6C# has accumulated so much syntactic sugar and features that it feels like three different languages. Inconsistencies between similar features (e.g., primary constructors vs. records, different object initialization methods) make the language harder to teach, learn, and use consistently.
Performance optimization complexity and poor benchmarks
6Identifying and fixing performance bottlenecks in C# is difficult, requiring deep understanding of memory management and resource allocation. C# ranks 22nd in TechEmpower performance benchmarks, necessitating use of profiling tools like dotTrace.
Extension method confusion and undocumented dependencies
5Developers unfamiliar with extension methods encounter compiler errors when viewing pre-written code that uses them. This causes confusion about missing libraries or version mismatches when the actual issue is an undeclared extension method, wasting significant debugging time.
Unnecessary abstractions and over-engineering
5C# developers create overly generic or abstract code anticipating future requirements, leading to unmaintainable solutions. This introduces state ownership issues, implicit workflows, and feature creep that obstructs rather than assists code clarity and testability.
Improper LINQ query patterns and exception handling
5Developers use Where().First() instead of FirstOrDefault() or apply First() with conditions inefficiently, leading to unnecessary exceptions when no matching values are found. This is a common pattern mistake among C# developers.
Manual property mapping instead of using mapping libraries
5Developers manually rewrite object properties line-by-line instead of using established mapping libraries like AutoMapper. This is tedious, error-prone (easy to omit properties), and leads to maintenance difficulties with complex object models.
Poor IDE and downstream integration of C# language features
5New C# language features (top-level statements, global includes) are sometimes delivered with incomplete IDE polish, integration issues, and downstream problems in ASP.NET Core. The implementation itself works, but the tooling and delivery experience suffers.
Ecosystem Centralization Around Microsoft
5While .NET is open source, the ecosystem is heavily driven by Microsoft's strategic goals, making it difficult to integrate with non-.NET tools and reducing appeal for developers working outside the Microsoft stack.
Legacy Codebase Verbosity and Boilerplate
5Many existing C# projects contain excessive boilerplate and outdated patterns, requiring developers entering the ecosystem to navigate legacy code that doesn't reflect the language's modern capabilities.
LINQ misunderstanding and misuse
5Many C# developers either don't know about LINQ or misunderstand its capabilities beyond database querying. Developers continue using iterative statements instead of LINQ for collection manipulation, missing opportunities for cleaner, more concise code, though performance trade-offs exist.
Imperative Code Patterns Reduce Maintainability
4Developers frequently write manual collection iterations and imperative statements for simple operations instead of using declarative alternatives, resulting in less maintainable and unnecessarily verbose code.
Limited Presence in Startup and Open-Source Communities
4C# has less visibility in startup environments and open-source communities compared to JavaScript, Python, or Go, limiting exposure to cutting-edge tools and reducing collaboration opportunities outside enterprise settings.
Empty Collection Allocation Overhead
4Habitually allocating new empty collections (arrays, lists) instead of using cached singletons causes unnecessary memory pressure and increases Garbage Collection overhead, especially in high-frequency loops.
Dictionary Resizing and Rehashing Performance Impact
4Failing to preset Dictionary capacity causes expensive Resizing and Rehashing operations when capacity is exceeded, significantly increasing memory allocation overhead if data volume is known.
Confusing .NET and C# naming conventions
4Microsoft's rebranding of .NET Core to .NET and frequent changes to target framework monikers (e.g., .netcoreapp3.X to .net5+) creates confusion. The naming strategy makes it harder for developers to understand version compatibility, especially when .NET Framework 4.0 still exists alongside .NET 5+.
C# perceived performance disadvantage vs. lower-level languages
4In performance-critical contexts requiring ultra-lightweight operation or low-level control, developers perceive C# as having a heavier runtime or memory footprint, pushing them toward languages with lower-level access. This affects adoption in performance-sensitive domains.
Reinventing collections instead of using .NET libraries
4Developers unnecessarily create custom collection objects when comprehensive built-in options exist in .NET, including specialized collections like persistent tree data structures and priority queues. This wastes time and introduces maintenance burden.