Comment l'agilité peut-elle être appliquée aux applications impliquant un traitement complexe?


12

La plupart de la littérature sur l'agile semble être orientée vers les applications métier de type CRUD où l'utilisateur est à peu près au courant de ce qui se passe en coulisses. (C'est bien parce que la plupart du code en cours d'écriture appartient probablement à cette classe.)

Pour ce type d'application, la relation entre les user stories (exigences) et les tâches de développement est généralement simple: il suffit de diviser la user story en quelques tâches.

Mais il existe un autre type d'application où la majeure partie du code doit traiter des traitements complexes qui ne sont pas directement visibles par l'utilisateur. Des exemples seraient:

  • Compilateurs
  • Systèmes d'analyse d'images de voitures autonomes
  • Systèmes de simulation d'écoulement de fluide

Ici, il peut être très difficile de relier les tâches et les histoires d'utilisateurs. Existe-t-il des techniques pour surmonter ce problème ou s'agit-il simplement de quelque chose que nous devons accepter et en tirer le meilleur parti?


6
Pas le downvoter, mais je suppose que c'est parce que la question est basée sur une fausse prémisse. Les systèmes complexes de l'OMI sont encore plus adaptés à un développement de style Agile en raison du fait qu'ils sont plus complexes. Plus le système est complexe, plus il est susceptible d'avoir de nouvelles exigences. Agile SwDev a été créé pour mieux gérer les nouvelles exigences.
RubberDuck

4
@RubberDuck: "Je suppose que c'est parce que la question est basée sur une fausse prémisse.": OMI, cela motiverait une réponse dans laquelle la fausse prémisse est expliquée, pas un downvote.
Giorgio

Que l'utilisation comprenne ou non la logique est totalement hors de propos pour le processus agile. Il appartient à l'équipe de mapper une user story à une implémentation ET d'estimer combien de temps cela prendra. Si c'est compliqué et / ou beaucoup de travail, l'équipe peut diviser l'histoire en plus petites. Le type de logique n'a pas d'importance.
Martin Maat

2
"La plupart de la littérature sur l'agile semble être orientée vers les applications commerciales de type CRUD" - Et la plupart de la littérature sur Java semble être orientée vers l'impression de la chaîne "Hello World" sur la console (ou une alternative de calcul du nième numéro de Fibonacci). Cela ne signifie pas que c'est la seule chose pour laquelle Java est bon. La raison est la même dans les deux cas: il est difficile d'expliquer des exemples réalistes dans un article de blog ou un tutoriel. [Remarque: il s'agit d'un commentaire hyperbolique essayant d'illustrer la base de la fausse prémisse.]
Jörg W Mittag

1
Agile ne nécessite ni tâches ni user stories. Vous pouvez utiliser n'importe quelle forme de spécification que vous souhaitez. Le tout est la flexibilité.
Frank Hileman

Réponses:


9

Cela s'est avéré plus long que ce que j'avais prévu (cela avait commencé comme un commentaire), mais j'espère que la longueur / les détails ajoutés sont utiles et que vous les trouvez justifiés.

Agile n'est pas spécifique aux applications CRUD

La plupart de la littérature sur l'agile semble être orientée vers les applications métier de type CRUD où l'utilisateur est à peu près au courant de ce qui se passe en coulisses. (C'est bien parce que la plupart du code en cours d'écriture appartient probablement à cette classe.)

Je pense que c'est parce qu'il est plus facile de créer des exemples faciles à suivre de ce type, pas vraiment parce que la méthodologie vise ces types de systèmes. Si vous créez un exemple pas si facile à suivre, vous risquez de coincer le lecteur en essayant de comprendre l'exemple alors que votre point était censé enseigner au lecteur des concepts agiles.

User Stories! = Exigences

Pour ce type d'application, la relation entre les user stories (exigences) et les tâches de développement est généralement simple: il suffit de diviser la user story en quelques tâches.

Une user story n'est pas la même chose qu'une exigence. Certes, il peut y avoir un certain chevauchement selon le niveau de «haut niveau» de l'exigence, mais généralement pas la même. J'ai l'impression que vous rencontrez le même écueil que beaucoup de mes anciens managers sont tombés: penser aux user stories simplement comme synonymes de "requirements", ce qui est similaire au moment où les utilisateurs SVN tentent de passer à Git, mais gardez penser en termes de SVN. (Ils rencontrent ensuite des problèmes en raison des mauvaises hypothèses de départ.)

À mon humble avis, une différence clé entre les exigences et les histoires d'utilisateurs est que les exigences spécifient, en détail, comment certains composants du système doivent se comporter; ce sont des spécifications qui incluent les entrées, les sorties, les hypothèses / conditions préalables, les exceptions possibles soulevées, etc. Elles se concentrent sur ce que fait le système .

OTOH, les user stories se concentrent sur le résultat attendu pour l'utilisateur final sans essayer de créer une spécification comportementale détaillée pour les composants du système. Ils se concentrent sur l' expérience utilisateur attendue .

Ce que j'avais l'habitude de faire, et c'était une pratique adoptée par mon équipe, était de décomposer les user stories en tâches. Vos tâches peuvent être aussi précises ou vagues que vous le souhaitez / en avez besoin, mais elles sont censées être vos indicateurs de progrès pour le travail réel accompli pour amener l'histoire à un état terminé.

Exemple

Je me souviens à peu près des États-Unis sur lesquels j'ai travaillé il y a des années où les utilisateurs devaient auto-assigner des cas de test afin que tout le monde dans l'équipe sache sur quels TC ils travaillaient pour éviter les efforts en double; l'interface utilisateur était une application Web (n interne). L'utilisateur n'a vu qu'un bouton, mais l'histoire a été divisée en plusieurs tâches qui comprenaient des détails de mise en œuvre technique, etc.

Visibilité utilisateur

Mais il existe un autre type d'application où la majeure partie du code doit traiter des traitements complexes qui ne sont pas directement visibles par l'utilisateur.

Est-il possible de le rendre visible d'une manière ou d'une autre pour l'utilisateur?

Prenons un GPS. Lorsque vous avez manqué votre tour, vous ne verrez pas le processus de recalcul de l'itinéraire réel, mais l'utilisateur reçoit des commentaires utiles (par exemple, "Recalculer ...").

Les compilateurs peuvent afficher des avertissements ou des erreurs, ou inclure de nouveaux paramètres / options dans l'interface graphique pour que les utilisateurs voient que quelque chose de nouveau a été ajouté. Je pense que les utilisateurs des compilateurs seraient des programmeurs, non? Ne verraient-ils pas le soutien d'une nouvelle norme ajoutée?

Bien que la prise en charge d'une nouvelle norme soit probablement au niveau des fonctionnalités et devrait être décomposée en user stories, vous êtes-vous assuré qu'au moins dans certains cas, vous n'essayez pas d'utiliser des stories alors que vous devriez plutôt utiliser des fonctionnalités ?

L'analyse d'image dans une voiture pourrait être formulée de manière à permettre à l'utilisateur de savoir que les chances de se retrouver dans un accident de voiture ont été réduites. Par exemple:

En tant que passager dans une voiture autonome, j'ai besoin que la probabilité que le véhicule provoque un accident en s'écrasant sur un objet non reconnu soit aussi proche de zéro que possible, afin de pouvoir voyager plus en toute sécurité.

Que les États-Unis capturent, à un niveau élevé, des choses que vous devriez normalement spécifier en utilisant une combinaison d'exigences fonctionnelles et non fonctionnelles, y compris la sécurité, la sûreté, etc.

Cependant, une exigence pourrait concerner davantage le système; par exemple:

La fonction abcdu composant Adoit avoir la valeur du seuil de tolérance diminuée dans l'algorithme de comparaison d'images pour mieux détecter les objets se déplaçant lentement.

Pour moi, ce serait facilement une tâche dans l'histoire de l' utilisateur je l' ai mentionné ci - dessus, intitulé quelque chose comme: Diminution de la tolérance en fonctionA.abc et inclure d' autres détails pertinents en elle.

Pour un système de simulation fluide, vous pouvez même avoir une barre de progression qui fournit des informations sur les tâches d'arrière-plan que le système effectue, si cela est logique. (Il existe toujours un moyen d'informer l'utilisateur de quelque chose, bien que vous souhaitiez peut-être éviter le spam.)

Je ne connais pas assez les domaines particuliers que vous avez mentionnés pour trouver des exemples meilleurs et / ou plus réalistes, mais s'il y a un point à retenir ici, c'est que vous pouvez utiliser différentes façons de fournir aux utilisateurs des commentaires sur quelque chose de moins visible que le système pourrait faire, c'est-à-dire qu'il pourrait y avoir des moyens de rendre les choses invisibles un peu plus visibles. (Même si cela se résume à la rédaction d'un ensemble de notes de publication qui documente à quel point les performances du système sont désormais plus rapides grâce à vos efforts, etc.)

Relation entre les histoires et les tâches

Ici, il peut être très difficile de relier les tâches et les histoires d'utilisateurs.

Notre approche était de garder les histoires d'utilisateurs concentrées sur ce qu'était la demande, pourquoi elle avait été faite et ce qui devait être vrai pour considérer que les États-Unis étaient «terminés». Le comment a toujours été exclu des États-Unis et laissé au (x) développeur (s).

Le ou les développeurs décomposeraient le problème décrit aux États-Unis en un ensemble de tâches sur lesquelles ils travailleraient.

Je dis cela comme quelqu'un qui, pour la plupart, a fait de la programmation côté serveur, qui est probablement aussi "invisible" que vous pouvez obtenir pour l'utilisateur final.

En fonction de ce que je devais faire, j'utilisais parfois AJAX pour afficher une animation / gif de "chargement ..." simple afin que l'utilisateur sache qu'il devait attendre un peu avant que quelque chose d'autre se termine, sans avoir la mauvaise impression. Parfois, c'était aussi simple que cela. Une tâche pour cela serait appropriée.

Paradigme, pratique et expérience différents

Existe-t-il des techniques pour surmonter ce problème ou s'agit-il simplement de quelque chose que nous devons accepter et en tirer le meilleur parti?

Au-delà de l'acceptation du changement de paradigme, de la pratique et de l'expérience accumulée, probablement pas grand-chose de plus à dire. J'ai souvent vu des gens essayer de prendre des raccourcis tout au long du processus. Je déconseille cela, surtout si vous commencez. À mesure que vous obtenez plus d'expérience, vous pouvez permettre une certaine flexibilité, mais évitez de vous affaiblir.

Compte tenu de votre formulation précédente, vous pensez toujours aux histoires comme si elles étaient des «exigences renommées», ce qui, je pense, est une fausse hypothèse. Je pense que c'est le symptôme d'un problème plus profond concernant les différences fondamentales entre les approches Agile et non Agile.

Deuxièmement, je pense que vous devez accepter que l'agilité est un changement de paradigme par rapport à la cascade, ce qui signifie que, si le processus a des objectifs similaires, ils s'y prennent de manières très différentes. (Pensez SVN vs Git, si cela aide.)

Essayez d'améliorer votre compréhension actuelle des différences conceptuelles entre les exigences et les user stories et acceptez que ce n'est pas la même chose.

Tirer des leçons de vos sprints - Rétrospectives

Ce que je ne saurais trop insister, c'est la rétrospective entre Scrum Master et Developers à la fin de chaque sprint. C'est l'endroit où ils discutent des choses qui "se sont bien passées" ou "qui ne se sont pas bien passées" d'une manière honnête / transparente, et quels changements réalisables seront mis en œuvre pour le prochain sprint afin de résoudre les points "qui ne se sont pas bien passés" .

Cela nous a permis de nous adapter et même d'apprendre des expériences des uns et des autres, et avant que nous le sachions, nous nous étions considérablement améliorés, mesurés par la cohérence générale de la vitesse de notre équipe.


"User Stories! = Requirements": Je ne voulais pas dire que les deux sont synonymes. Toutes les exigences ne sont pas une histoire d'utilisateur. Mais je pense que toutes les histoires d'utilisateurs sont des exigences. Je considère les exigences comme une structure hiérarchique où les user stories sont un niveau de détail spécifique. Accepteriez-vous?
Frank Puffer

@FrankPuffer Je pense que voir les user stories comme si elles étaient d'un niveau différent dans une hiérarchie d'exigences, c'est essentiellement mélanger différents concepts. Du côté Agile, une hiérarchie ressemble plus à: Thèmes >> Epics >> Fonctionnalités >> User Stories >> Tâches . Les exigences sont généralement divisées en exigences fonctionnelles et non fonctionnelles dans l'approche Waterfall plus traditionnelle, mais je n'ai pas rencontré de hiérarchie réelle; cela dit, je ne serais pas surpris si quelqu'un décomposait récursivement une exigence en «sous-exigences» plus petites. Dans tous les cas, vous mélangez différents concepts.
code_dredd

Les systèmes de gestion des exigences comme PTC Integrity traitent les exigences comme une hiérarchie. Cela peut être un avantage lors de la mise en correspondance des exigences avec un document de spécification.
Frank Puffer du

@FrankPuffer Oui, c'est pourquoi j'ai dit que je ne serais pas surpris. Cela dit, je me demande si ma réponse a clarifié quoi que ce soit pour vous. L'analogie SVN vs Git était-elle utile? (Cela suppose que vous êtes familier avec les deux systèmes, ce qui semblait raisonnable dans un contexte de développement logiciel.)
code_dredd

Ok, j'ai mal interprété le «ne serait pas» comme «le ferait». Et oui, je trouve votre réponse très utile et je l'accepterai probablement. J'ai probablement juste besoin d'un peu de temps pour m'habituer à l'idée de ne pas considérer les user stories comme des exigences.
Frank Puffer

4

Les principes agiles peuvent certainement être appliqués dans ces cas. Comment?

  • Les compilateurs peuvent avoir de nouvelles fonctionnalités linguistiques ajoutées ultérieurement dans des user stories distinctes
  • Les systèmes d'analyse d'images peuvent avoir de nouvelles fonctionnalités ajoutées en tant que différentes classifications d'images
  • Les systèmes de simulation d'écoulement de fluide peuvent prendre en compte différents aspects du modèle dans leurs simulations

Vous n'avez pas à manger l'éléphant entier en une seule bouchée. Agile vous demande simplement de montrer que vous avez nettoyé votre assiette avant la prochaine portion d'éléphant.


Je pense néanmoins qu'il restera une ou plusieurs user stories qui nécessitent de nombreuses fonctionnalités dites de base. Souvent, ils ne rentreront pas dans un seul sprint. Comment cela devrait-il être géré?
Frank Puffer

1
Vous mesurez la réussite avec des applications exécutables que les clients peuvent tester, voir ou toucher. Si tel est le cas, donnez-leur un jouet qui génère ainsi le sens du progrès. Par exemple, vous ne pouvez probablement pas libérer une voiture autonome au premier sprint, mais vous pouvez faire une démonstration de la façon dont votre algo fait reconnaître les gens. Plus tard, comment il reconnaît les signaux et les animaux. Plus tard, comment il mesure les distances, les tailles, etc ... La voiture autonome est loin, très loin dans un sprint à distance qui sait si cela arrivera jamais.
Laiv

2
@Laiv: Ce serait bien mais je ne sais pas si cela fonctionne. En fait, après les premiers sprints, le logiciel ne pourra plus rien faire pour le client. Vous aurez du progrès technique mais il sera difficile, sinon impossible, de le communiquer au client.
Frank Puffer

2
Pourquoi? Parce qu'il a été écrit sur Stone par une personne étrangère à votre projet? Je m'attends à ce qu'Agile s'adapte à mes besoins, pas autrement. Si je dis que je peux vous apprendre à patiner en 4 semaines et qu'une fois le 5 vous échouez toujours en position debout, cela signifie-t-il que vous n'apprenez pas à patiner? Ou tout simplement que cela vous prendra un peu plus de temps? Le manifeste Agile n'a pas été écrit sur la pierre ni les règles du Scrum ne sont les Commandements Tend.
Laiv

2
Le but des sprints est de faire participer votre client à vos itérations. Pour communiquer avec eux en utilisant le code livré. Pas des plans et des promesses. Cela vous oblige à vous concentrer sur la résolution du problème. Il ne nécessite pas que la première chose que vous livrez soit gravée dans la pierre. Cela nécessite que vous prouviez que vous valez la peine de payer chaque sprint.
candied_orange

1

Je trouve que les personnes qui adhèrent strictement aux histoires d'utilisateurs se livreront simplement à un exercice très stupide de trouver des moyens farfelus dans lesquels les changements techniques d'arrière-plan affectent l'utilisateur (à l'insu de l'utilisateur, bien sûr, car ils ne sont que naïfs) utilisateur et vous parlez de changements complexes dans votre pipeline d'analyse de données ou quelque chose de ce genre) ou ils seront tout simplement perdus pour "comment pouvons-nous organiser ce travail!?!"

Je pense que la solution évidente est d'être plus pragmatique. Si le travail est de nature très technique et n'a pas d'impact particulièrement notable sur l'utilisateur, ne perdez pas de temps à essayer d'expliquer comment il fonctionne. Il suffit de choisir une manière évidente et simple dont elle peut bénéficier aux utilisateurs, puis d'orienter l'histoire autour des détails nécessaires aux développeurs pour faire leur travail. Je trouve cela extrêmement frustrant lorsqu'un PO insiste pour ne pas avoir d'informations techniques dans l'histoire alors que c'est absolument nécessaire. Ce n'est tout simplement pas une vue très holistique de ce qu'est réellement cet artefact (l'histoire). Comme ils pensent que cela existe juste pour eux, dans la plupart des cas, c'est aussi important pour les ingénieurs.

Pour la plupart de ces tâches techniques, il y a quelques fruits faibles en ce qui concerne l'impact sur l'utilisateur, que ce soit pour améliorer l'efficacité afin que les livraisons futures soient plus rapides, améliorant les performances, la fiabilité, etc. Ce n'est pas vraiment ce à quoi les gens ont tendance à penser lorsqu'ils pensent à des `` user stories '', mais si l'entreprise veut comprendre pourquoi vous contractez une dette technique ou quelque chose à cet effet, ces explications sont généralement les plus simples à fournir.

tl; dr ne laissez pas un scrumnazi vous compliquer la vie simplement parce qu'il est trop carré pour s'adapter. Être adaptatif est un concept fondamental de l'agilité après tout. Une stricte adhésion à Scrum ou Agile va généralement à l'encontre du visage ou du pragmatisme et de la praticité (ce qui fonctionne le mieux).


Je suis tout pour être flexible, mais franchement, l'utilisateur ne se soucie pas particulièrement des détails techniques, tant que leurs histoires sont satisfaites. Vous n'avez pas besoin d'être "strictement agile" pour avoir de bonnes exigences; et par bonnes exigences, je veux dire des exigences qui sont chacune accompagnées d'un test d'acceptation qui prouve sans ambiguïté que l'exigence est satisfaite. Les gens qui "se livrent à des exercices très stupides" le font évidemment mal, mais cela ne signifie pas qu'ils suivent une certaine notion de "scrum strict".
Robert Harvey

@RobertHarvey Je suis d'accord que les détails techniques ne sont pas pertinents pour l'utilisateur, mais mon point est que les artefacts contenant des histoires d'utilisateurs ont un objectif plus large que de simplement communiquer comment les choses fonctionnent pour l'utilisateur (ou du moins ils devraient). Comment appliquer les exigences liées à l'architecture, l'extensibilité, les performances, etc., si elles écrivent uniquement des histoires d'utilisateurs? L'adoption d'une approche purement «user story» encourage les développeurs à écrire du code de mauvaise qualité. Et ce ne sont pas les utilisateurs qui lisent les `` user stories '', ce sont les devs et les qa, c'est stupide d'exclure délibérément des informations pertinentes parce que c'est technique.
evanmcdonnal

0

Je pense que le problème est de donner aux user stories un sens qu'elles n'ont pas. Scrum utilise le terme PBI, ou Product Backlog Item, qui, je pense, est infiniment meilleur. Les PBI suivront souvent un format de user story, par exemple, vous pourriez avoir un PBI comme "Les abonnés devraient pouvoir voir les détails de leur abonnement", mais vous pourriez également avoir tout aussi facilement un PBI comme "Créer une procédure stockée pour obtenir les détails de l'abonné" ".

Les user stories sont un outil . Ils vous aident à créer des descriptions de fonctionnalités et des exigences basées sur la mise à la place d'un utilisateur. Mais, tout comme une clé est inutile lorsque vous devez accrocher une photo, il y a des moments où vous n'avez peut-être pas besoin d'une histoire d'utilisateur.

Cela dit, de nombreuses équipes jouent en fait rapidement et librement avec la partie "utilisateur". Ils peuvent avoir des "user stories" comme "En tant que développeur, je dois pouvoir appeler une procédure stockée pour obtenir les détails de l'abonné", essentiellement une "histoire de développeur" pour ainsi dire. C'est une option tout aussi valable, mais personnellement, je dis que tant que vous pouvez décrire ce qui doit être fait et proposer un ensemble de critères d'acceptation, peu importe si vous avez une véritable histoire d'utilisateur derrière ou non.


1
Je ne suis pas d'accord avec cela, sauf dans des cas très étranges et rares. Dans Scrum, le COMMENT est du ressort de l'équipe de développement et non du propriétaire du produit. Pourtant, le propriétaire du produit devrait être responsable des PBI. Ainsi, un PBI comme "créer une procédure stockée" enlève à l'équipe de développement le droit de choisir le COMMENT. Les PBI, qu'ils soient ou non des user stories, doivent expliquer quelle est la valeur commerciale de l'IBP. La création d'une procédure stockée n'est pas une question de valeur commerciale, c'est une question de mise en œuvre.
RibaldEddie

Ce n'est pas le propos. C'était juste un exemple. Vous pouvez simplement changer "procédure stockée" en quelque chose de générique comme "un moyen". Le fait est que cela ne doit pas nécessairement être une histoire d'utilisateur.
Chris Pratt

l'intérêt d'un exemple est d'être un exemple. Si votre exemple "n'est pas le point" alors quel est le point de l'exemple ??
RibaldEddie

-3

Ces types d'applications sont exactement ceux où une expertise différente est présente et se développeront davantage. Les membres de l'équipe auront en raison de formations différentes, de différents projets de passe-temps et de différentes expériences professionnelles passées des compétences différentes. De plus, si quelqu'un développe un morceau de code particulier, le développeur peut être censé être celui qui connaît le mieux le code. Il peut donc être judicieux de confier d'autres tâches de développement impliquant le même morceau de code au même développeur.

Dans le processus agile le plus populaire, Scrum, il y a la planification du poker où chaque tâche est assignée un niveau de difficulté. Le niveau de difficulté ne dépend pas de la personne qui effectue cette tâche selon le processus. Ensuite, pendant le sprint, les gens sont considérés comme homogènes de sorte que chaque personne devrait pouvoir choisir chaque tâche et la mettre en œuvre. Dans les projets similaires à CRUD, cette hypothèse est valable. Mais dans les projets très complexes et difficiles, ce n'est certainement pas le cas.

Je n'utiliserais pas un processus agile pour ce genre de projets. Votre meilleur choix est d'éviter tout processus formel et d'utiliser simplement une bonne gestion de projet. Lorsque vous décidez qui implémente une fonctionnalité particulière, déterminez qui possède les meilleures compétences nécessaires pour cette fonctionnalité et la meilleure connaissance du code existant. Aucun processus n'est nécessaire pour cela. Vous voudrez probablement rédiger de bons documents de conception pour toutes les fonctionnalités et les tenir à jour. Notez que je ne fais pas la promotion d'un modèle de type cascade ici: les documents de conception ne seront pas tous écrits au début du projet; vous allez plutôt écrire de nouveaux documents de conception car de nouvelles fonctionnalités sont nécessaires.


5
Pas vraiment lié à ma question, mais laisser toujours celui qui a les meilleures compétences implémenter une fonctionnalité peut être dangereux. Cela entrave la diffusion des connaissances au sein de l'équipe. Si une maintenance est nécessaire et que l'expert a quitté l'équipe ou est temporairement indisponible, vous aurez des ennuis.
Frank Puffer

@FrankPuffer Pour certains des types de systèmes que vous avez énumérés, par exemple les voitures autonomes, si l'expert quitte l'équipe, vous êtes en difficulté. Période. Bien qu'il ne soit probablement pas vrai que le codage doive être centralisé, il est également complètement déraisonnable de supposer une "diffusion des connaissances" adéquate pour permettre le remplacement de l'expert dans un délai raisonnablement court. Ce sont des gens qui auront passé plus d'une décennie à faire des recherches sur le problème et qui sont vraisemblablement proches du sommet de leur domaine. Vous aurez probablement besoin de plusieurs personnes comme celle-ci avec des compétences différentes. De tels projets sont intrinsèquement risqués.
Derek Elkins a quitté le SE
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.