
C# ou F# : que choisir pour un code plus robuste et maintenable ?
Comparatif pragmatique entre C# et F# sous l’angle du Software Craftsmanship : expressivité, testabilité, robustesse, modélisation métier. Sans prosélytisme, juste des faits.
Publié le 13 avril 2025
Quand on parle de qualité logicielle, les discussions reviennent souvent à des principes comme la lisibilité, la testabilité ou encore la capacité à faire évoluer un code sans douleur. Autrement dit : du code de qualité. Dans l’univers .NET, C# est le langage par défaut. Pourtant, F#, souvent perçu comme marginal, propose une autre approche, parfois plus alignée avec ces valeurs.
Plutôt que d'opposer les deux, cet article propose un comparatif honnête, basé sur l’expérience terrain. L’objectif : comprendre dans quels cas l’un ou l’autre langage peut favoriser un meilleur Craft.
1. Expressivité et verbosité
C# a beaucoup évolué ces dernières années. L’arrivée des records, des init
, du pattern matching ou des lambdas simplifiées a permis de rendre le langage plus expressif. Pourtant, la structure orientée objet, avec ses classes, accesseurs et attributs, reste assez verbeuse.
F#, à l’inverse, se concentre sur la concision. Tout est conçu pour limiter le bruit syntaxique. Le code devient plus lisible à condition d’adhérer au style fonctionnel. Un simple type Person avec deux champs s’écrit en une ligne, sans accesseurs explicites.
type Person = { Name: string; Age: int }
En C#, il faut écrire :
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
Ou encore en C# 9 :
public record Person(string Name, int Age);
En pratique :
- C# est plus verbeux, mais familier pour la majorité des équipes .NET.
- F# permet d’exprimer plus avec moins de code, mais nécessite une adaptation initiale.
2. Modélisation métier
Modéliser un domaine de manière claire et exhaustive est un pilier du Software Craft. C# permet une bonne structuration, notamment avec les records. Cependant, la gestion des cas métier complexes (types fermés, alternatives, etc.) est plus difficile.
F# se distingue ici avec les discriminated unions, qui permettent d’encoder de manière explicite tous les cas possibles d’un type métier. Par exemple, un résultat d’authentification peut être soit un succès, soit une erreur. Ce modèle se code naturellement en F#, avec une exhaustivité imposée par le compilateur.
type AuthResult =
| Success of string
| Error of string
En C#, il faudrait recourir à des classes ou des enums, ce qui peut rendre le code moins clair et plus sujet aux erreurs.
public class AuthResult
{
public string? Success { get; }
public string? Error { get; }
private AuthResult(string? success, string? error)
{
Success = success;
Error = error;
}
public static AuthResult CreateSuccess(string token) => new(token, null);
public static AuthResult CreateError(string message) => new(null, message);
}
En pratique :
- C# est adapté pour les modèles standards, mais nécessite du code complémentaire pour forcer la couverture des cas.
- F# pousse naturellement à une modélisation explicite et sécurisée.
3. Testabilité et séparation des effets
Le Craft, c’est aussi écrire du code facile à tester. C# permet cela avec des outils matures (xUnit, Moq, etc.) et des pratiques comme l’injection de dépendance. Mais cette approche peut vite devenir lourde : mocks, services, interfaces à foison.
F# pousse naturellement à écrire des fonctions pures. Les effets (accès DB, I/O) sont isolés à la marge du système. Résultat : une grande partie du code métier peut se tester directement, sans aucun framework.
En pratique :
- En C#, tester demande parfois de l’architecture (interfaces, mocks, etc.).
- En F#, on teste souvent des fonctions pures, sans surcoût structurel.
4. Refactoring et robustesse
Un code de qualité est un code qu’on peut faire évoluer sereinement. En C#, les outils comme Resharper ou Rider facilitent le refactoring. Mais certaines erreurs restent invisibles à la compilation, notamment dans les switches non exhaustifs ou les usages de null
.
F#, de par son système de types et son pattern matching exhaustif, rend les refactorings plus sûrs. Supprimer un cas d’un discriminated union oblige à mettre à jour tous les endroits concernés. Pas d’oubli possible.
En pratique :
- C# est bien outillé, mais la sécurité du typage reste partielle.
- F# offre un filet de sécurité plus strict, ce qui favorise des évolutions sereines.
5. Intégration et écosystème
C# domine naturellement l’écosystème .NET. Tous les frameworks, les tutos, les librairies, les IDE sont pensés d’abord pour lui. C’est un énorme avantage au quotidien, surtout en contexte d’équipe ou de projet long.
F# reste moins utilisé. Pourtant, il s’intègre parfaitement à l’écosystème .NET : on peut utiliser les mêmes packages NuGet, partager du code avec C#, profiter de Visual Studio ou Rider. Mais la communauté est plus petite, et certains frameworks (ex : WinForms, MAUI) restent difficilement exploitables en F#.
En pratique :
- C# bénéficie d’un écosystème très mature et universel.
- F# s’intègre bien mais demande parfois plus de débrouillardise.
6. Scénarios recommandés
Il ne s’agit pas de dire que F# est "meilleur" que C#. Chaque langage a ses forces, selon le contexte.
Utiliser C# :
- Pour des projets orientés objet traditionnels.
- Quand l’équipe est 100 % C# et peu ouverte au fonctionnel.
- Pour des apps riches côté client (MAUI, WPF…).
Utiliser F# :
- Pour des modules métiers isolés, à forte logique.
- Si la testabilité, la robustesse et la concision sont prioritaires.
- En architecture modulaire (microservices, moteurs de règles, outils d’analyse, etc.).
Conclusion
F# ne remplacera pas C#, mais il peut enrichir la boîte à outils d’un développeur .NET soucieux de produire du code propre et maintenable. Il n’est pas nécessaire d’adopter F# pour tout faire. Mais dans certains contextes, surtout quand la complexité métier s’accumule, il peut aider à écrire un code plus clair, plus fiable, et plus durable.
Un bon développeur Craft sait reconnaître l’outil le mieux adapté à la situation. C’est exactement l’esprit de cet article.