
Les bonnes pratiques QML
- Nathan PRIOR
- Bonnes pratiques
- 26 août 2025
En bref
Bonnes pratiques QML pour écrire un code clair, maintenable et performant avec Qt Quick

Bonnes pratiques QML pour écrire un code clair, maintenable et performant avec Qt Quick
Dans cet article, je partage avec vous quelques bonnes pratiques QML qui vous permettront d’éviter les pièges classiques et de construire des interfaces à la fois claires, réactives et faciles à faire évoluer.
Combien de fois avez-vous entendu (ou pensé) : “Ce code est un bordel, on devrait tout réécrire !”
Que vous codiez en QML, Python, JavaScript ou autre, cette phrase est un symptôme : quelque part, les bonnes pratiques n’ont pas été appliquées. Ou pire, elles ont été ignorées au profit de la rapidité.
Dans cet article, je partage avec vous ce que j’ai appris (parfois à la dure) sur les bonnes pratiques, les conventions et les pièges à éviter.
Pour moi, c’est une habitude ou une règle qui apporte un bénéfice tangible :
Exemple : Nommage clair des variables, séparation des responsabilités, ordre de déclaration des propriétés… Des détails ? Oui. Mais c’est comme l’huile dans un moteur : sans elle, tout grince.
⚠️ Attention : Certaines bonnes pratiques demandent un effort initial. Un junior peut les trouver “inutiles”, un sénior y verra un gain de temps sur le long terme. C’est normal : leur valeur se révèle avec l’expérience.
Une convention, c’est une règle arbitraire mais partagée. Peu importe que vous utilisiez des tabulations ou des espaces, l’important est que toute l’équipe fasse de même.
Pourquoi ?
Mon conseil : Documentez vos conventions (un CONTRIBUTING.md ou un wiki suffisent). Ça évite les débats sans fin et les Merge/Pull Request bloquées pour des détails.
Les mauvaises pratiques sont comme des dettes techniques : on les contracte sans y penser, et un jour, elles nous explosent à la figure.
Quelques signes qui ne trompent pas :
Mon conseil : Prenez le temps de faire les choses bien, même si c’est plus long au début. Votre vous futur vous remerciera.
Et maintenant, place à la pratique ! Nous allons maintenant voir comment appliquer ces principes concrètement en QML (organisation, lisibilité, maintenabilité…). Mais d’abord, une question : Quelle est la pire mauvaise pratique que vous ayez vue (ou commise) ? 😉 N’hésitez pas à répondre en commentaire de cet article.
Mon cauchemar à moi ? Un projet où tous les fichiers étaient entassés à la racine – pas de dossiers, pas de structure, juste 200 ou 300 fichiers C++ perdus dans un même dossier. Résultat : une chasse au trésor quotidienne pour retrouver une classe. Le pire ? Les seniors de l’équipe trouvaient ça normal après 15 ans de développement, et les juniors en alternance croyaient dur comme fer que c’était la façon standard d’organiser un projet. Bref, l’enfer du développeur.
QML est un langage déclaratif et flexible, idéal pour créer des interfaces fluides et dynamiques. Mais cette liberté a un prix : sans rigueur, on se retrouve vite avec du code spaghetti, où la hiérarchie visuelle ne reflète plus la structure logique, et où les bindings (property: value) deviennent des pièges sournois.
Pourquoi QML est-il différent ? QML a une particularité : il laisse la possibilité de fusionner interface et logique (grâce au JavaScript intégré). Sans vigilance, cette flexibilité se retourne contre vous :
width: parent.width * someComplexFormula) rendent le comportement de l’application imprévisible et difficile à déboguer.Button qui calcule un prix ou valide un formulaire, c’est tentant… mais c’est aussi la porte ouverte aux bugs et à la dette technique.Conséquence ? Un code où tout dépend de tout, où une modification mineure casse trois écrans, et où personne n’ose plus toucher à rien.
Heureusement, quelques règles simples suffisent à éviter le chaos. Passons-les en revue, section par section.
Par où commencer ? Par l’organisation, bien sûr ! Une structure de projet claire, c’est comme une bonne fondation : si elle est solide, tout le reste suit. Sinon, préparez-vous à des nuits blanches de refactoring.
Dans un projet professionnel mixant C++ et QML, on se retrouve souvent avec une tonne de fichiers : .h, .cpp, .qml, .js, .png, .svg, .ttf, CMakeLists.txt, etc. L’enjeu ? Trouver un équilibre entre rigueur et flexibilité, car il n’y a pas de solution universelle. Voici deux exemples de structures que j’ai pu expérimenter, avec leurs forces et leurs limites.
project_name/
├── include/ # En-têtes C++
│ └── project_name/
│ └── cpp_folder/
│ └── MyClass.h
├── resources/ # Ressources statiques (images, polices, etc.)
│ ├── images/
│ └── fonts/
├── src/
│ ├── main.cpp # Point d'entrée C++
│ ├── cpp_folder/ # Implémentations C++
│ │ └── MyClass.cpp
│ └── qml/ # Code QML
│ └── qml_folder/
│ └── Component.qml
├── test/ # Tests unitaires
│ └── cpp_folder/
│ └── MyClassTest.h
├── .clang-format # Formatage automatique
├── .clang-tidy # Analyse statique
├── .gitignore
└── CMakeLists.txt # Configuration du build
Pourquoi ça marche ?
project_name/
├── app/ # Application principale (même structure que ci-dessus)
|
├── core_library/ # Librairie C++ (métier + technique, sous dépôt Git)
|
├── ui_library/ # Librairie QML (composants graphiques réutilisables, sous dépôt Git)
│ ├── token/ # Tokens de design (couleurs, polices, tailles)
│ ├── atom/ # Composants atomiques (boutons, champs de texte)
│ ├── molecule/ # Composants composites (section + titre + bouton)
│ ├── organism/ # Composants complexes (headers, footers)
│ └── resources/ # Ressources spécifiques à l’UI
|
├── modules/ # Modules indépendants (chaque module = un sous-projet Git)
│ ├── module1/
│ └── module2/
|
├── .clang-format
├── .clang-tidy
├── .gitignore
├── .gitmodules # Pour les sous-modules Git
└── CMakeLists.txt
Pourquoi cette structure ?
core_library et ui_library sont autonomes et peuvent être partagées entre plusieurs projets.À retenir :
README.md, Confluence, Notion, etc.).misc/ ou temp/ – ils deviennent vite des poubelles numériques.Je vous présente ici deux approches pour organiser vos fichiers QML. Libre à vous de les mixer selon vos besoins !
Idéale si votre application est orientée métier (ex. : une app de bons plans).
project_name/
└── qml/
├── header/ # En-tête de l'application
│ ├── Header.qml
│ └── SettingsPanel.qml
├── dashboard/ # Tableau de bord
│ ├── Dashboard.qml
│ └── MyGoodDeals.qml
├── menu/ # Menu de navigation
│ ├── Menu.qml
│ └── MenuItem.qml
├── feature/ # Features métiers
│ ├── profil/
│ │ ├── ProfilPage.qml
│ │ └── ProfilResumePanel.qml
│ └── deal/
│ ├── DealPage.qml
│ └── LastDealListPanel.qml
└── Main.qml # Point d'entrée QML
Pourquoi ?
Idéale si vous utilisez un design system ou une librairie de composants.
project_name/
└── qml/
├── token/ # Style de l'application et variables de base
│ ├── Fonts.qml
│ ├── Icons.qml
├── atom/ # Composants simples (atomiques)
│ ├── Button.qml
│ ├── Divider.qml
│ ├── SpecificButton.qml
├── molecule/ # Composants complexes (composés d'atomes)
│ ├── ProfilPanel.qml
│ └── DealCard.qml
├── organism/ # Panneaux entiers (composés de mollecules / atomes)
│ ├── Header.qml
│ └── Menu.qml
└── page/ # Pages entières
└── Dashboard.qml
Pourquoi ?
Mon conseil :
La lisibilité, c’est comme l’orthographe : tout le monde sait que c’est important, mais peu de gens en font une priorité. Pourtant, un code lisible, c’est un code maintenable, collaboratif et moins stressant.
Je ne vais pas vous ressasser les bonnes pratiques que vous connaissez déjà par cœur :
userProfileAvatar > img1.)Non, concentrons-nous maintenant sur ce qui est propre au QML.
En QML, l’ordre des déclarations compte. Pas pour le compilateur, mais pour vos collègues (et votre futur vous). Voici l’ordre que je recommande, testé et approuvé sur des projets réels :
id (toujours en premier)property)anchors, width, height)states)transitions)A copier-coller sans modération
import QtQuick // Imports Qt
import my.project // Plugin ou module interne
import "my/folder" // Import relatif
Item {
id: root // Toujours en premier !
//========================================================================
// Propriétés
//========================================================================
property bool isActive // false par défaut, donc pas de ré-assignation
property string userName // "" par défaut, donc pas de ré-assignation
//========================================================================
// Propriétés héritées
//========================================================================
anchors.fill: parent
width: 200
height: childrenRect.height
//========================================================================
// Signaux
//========================================================================
signal userUpdated(string newName)
//========================================================================
// Fonctions
//========================================================================
function calculateWidth() {
return root.width * 0.8
}
//========================================================================
// Eléments privées
//========================================================================
QtObject {
id: internal
property string userPwd
}
//========================================================================
// Objets enfants
//========================================================================
Rectangle {
color: "lightgray"
anchors.fill: parent
}
//========================================================================
// États
//========================================================================
states: [
State {
name: "active"
when: root.isActive
PropertyChanges { target: root; color: "blue" }
}
]
//========================================================================
// Transitions
//========================================================================
transitions: [
Transition {
from: "*"
to: "active"
ColorAnimation { duration: 200 }
}
]
}
Pourquoi cet ordre ?
propertiesPetit bonus :
// ====) pour délimiter les sections.property bool isActive: false).La maintenabilité, c’est l’art de ne pas se tirer une balle dans le pied dans 6 mois. Un code maintenable, c’est un code où :
Un principe fondamental : chaque élément de votre code — qu’il s’agisse d’un composant QML, d’une classe C++, ou de toute autre entité — doit avoir une seule responsabilité. Cela signifie qu’il doit répondre à un besoin précis et unique.
Pourquoi est-ce si important ? Combien de fois avons-nous vu des composants ou des classes surchargés, où s’entassent des fonctions aux responsabilités multiples ? Ce mélange de responsabilités rend le code difficile à maintenir, à tester et à faire évoluer. Un composant QML doit faire une chose, et une seule.
Il est crucial de souligner que la logique métier ne doit jamais être gérée par le QML. Le rôle du QML est de présenter l’interface utilisateur et de réagir aux interactions utilisateur, mais il doit rester découplé de la logique métier.
Cette séparation est d’ailleurs un pilier des architectures logicielles modernes, comme :
Exemple visuel : Dans le schéma ci-dessous, issu de la Clean Architecture, la couche UI (IHM) est clairement positionnée comme un plugin externe, distinct du domaine métier.

Pourquoi adopter cette approche ?
En résumé : Respectez le principe “une entité, une responsabilité”, et gardez votre QML exclusivement dédié à l’interface. La logique métier a sa place ailleurs.
Au final, la règle d’or (qui s’adapte d’ailleurs à la plupart des langages) :
“Un composant QML = une responsabilité. Point.”
Exemple concret :
// ProfilePage.qml (500 lignes)
Item {
// 1. Affiche le profil utilisateur
// 2. Gère la connexion API
// 3. Valide les champs du formulaire
// 4. Envoie des notifications
// ...
}
// ProfilePage.qml (50 lignes)
Item {
ProfileHeader { user: userManager.currentUser() }
ProfileForm { onSubmit: userManager.save() }
NotificationPopup { }
}
userManager (C++).Pourquoi ça marche ?
ProfileHeader peut être utilisé ailleurs.Un bon composant, c’est comme une boîte noire : on ne voit que ce qui est nécessaire pour l’utiliser, le reste reste caché. Mais pour y parvenir, il est indispensable de réfléchir aux entrées/sorties, à ce qui doit être public et ce qui doit rester privé avant même d’écrire une ligne de code.
La clé ? Adoptez une approche centrée sur l’utilisateur final de votre composant (que ce soit vous dans 6 mois ou un collègue). Demandez-vous :
Mon petit rituel (inspiré du TDD) : Avant de coder, j’écris souvent du pseudo-code en imaginant mon composant déjà terminé. Par exemple :
// Pseudo-code : Comment j''aimerais utiliser mon composant ?
UserProfile {
user: backend.currentUser // Entrée claire
editable: isAdmin // Option simple
onSaved: showSuccess() // Sortie explicite
}
Ensuite, je travaille à faire en sorte que ce pseudo-code devienne réalité. Cette méthode me force à :
Résultat ? Un composant plus simple à utiliser, à tester et à maintenir – et surtout, qui répond vraiment aux besoins de ceux qui vont s’en servir.
Exemple concret (repris et amélioré) :
// UserProfile.qml - API conçue "à l'envers"
Item {
id: root
// ===== API PUBLIQUE (ce que l'utilisateur regarde) =====
//========================================================================
// Propriétés
//========================================================================
required property User user // Données utilisateur (entrée)
required property bool editable // Mode édition (entrée)
//========================================================================
// Signals
//========================================================================
signal saved() // Événement de sauvegarde (sortie)
signal error(string) // Gestion des erreurs (sortie)
// ===== FIN API PUBLIQUE =====
// ===== IMPLÉMENTATION (intérieur de la boîte noire) =====
//========================================================================
// Eléments privées
//========================================================================
QtObject {
id: internal
function validate()
{
/* Logique interne */
}
}
//========================================================================
// Objets enfants
//========================================================================
Column {
TextField { text: root.user.name; enabled: root.editable }
Button {
text: "Enregistrer"
onClicked: {
if (internal.validate())
{
root.saved(root.user?.name ?? "");
}
else
{
root.error("Champs invalides");
}
}
}
}
}
Pourquoi cette approche change tout ?
En résumé :
Les bonnes pratiques, ce n’est pas du luxe ni de la théorie de puriste : c’est ce qui fait qu’un projet reste vivant et agréable à maintenir dans la durée.
En QML comme ailleurs, un code clair, découplé et lisible, c’est moins de bugs, plus de sérénité et une équipe qui avance sans se faire peur !
Si vous êtes arrivé jusque-là, vous avez déjà fait un grand pas : la prise de conscience.
La suite ? C’est l’application au quotidien — et c’est souvent là que le bât blesse. Parce qu’entre les deadlines, la dette technique et la réalité d’un projet, il est facile de retomber dans les travers que j’ai décrits.
💡 Pour approfondir le sujet, je vous recommande vivement ce dépôt GitHub qui rassemble de nombreux conseils et bonnes pratiques en QML. Vous y trouverez aussi bien des règles de style (nommage, indentation, organisation des fichiers) que des recommandations de conception (structuration des vues, utilisation pertinente des propriétés, optimisation des performances, etc.).
👉 QML Coding Guide – Furkanzmc
En tant que développeur et architecte freelance spécialisé en C++ / Qt / QML, j’accompagne mes clients pour :
👉 Si vous voulez appliquer ces principes à votre projet (ou simplement éviter qu’il parte en code spaghetti 😉), je serai ravi d’échanger avec vous.
📩 Me contacter