¿Cómo entrenarse para evitar escribir código "inteligente"? [cerrado]


75

¿Conoces esa sensación cuando solo necesitas presumir ese nuevo truco con Expressions o generalizar tres procedimientos diferentes? Esto no tiene que estar en la escala de Arquitectura Astronauta y, de hecho, puede ser útil, pero no puedo evitar notar que otra persona implementaría la misma clase o paquete de una manera más clara, directa (y a veces aburrida).

Me di cuenta de que a menudo diseño programas resolviendo el problema , a veces deliberadamente y otras por aburrimiento. En cualquier caso, generalmente creo sinceramente que mi solución es clara y elegante, hasta que veo evidencia de lo contrario, pero generalmente es demasiado tarde. También hay una parte de mí que prefiere suposiciones indocumentadas a la duplicación de código, y la inteligencia a la simplicidad.

¿Qué puedo hacer para resistir el impulso de escribir un código "inteligente" y cuándo debe sonar la campana que estoy haciendo mal ?

El problema se está volviendo aún más difícil, ya que ahora estoy trabajando con un equipo de desarrolladores experimentados, y a veces mis intentos de escribir código inteligente me parecen tontos, incluso después de que el tiempo disipa la ilusión de la elegancia.


55
ligeramente fuera de tema, pero thedailywtf.com/Articles/…

@ Joe: esto es muy sobre el tema, ¡gracias! He leído el artículo pero es un placer redescubrirlo ahora.
Dan

33
Depure una gran cantidad de código inteligente ... eso debería ser el truco.
Dan Olson

@Joe el enlace de la base de datos de bases de datos en ese artículo es para morirse.
jnewman

Respuesta corta: el código más corto y simple gana. Elimine la duplicación, pero no agregue capas "solo porque". La refactorización de Fowler puede darle una idea.
Kevin Cline

Respuestas:


54

El problema se está volviendo aún más difícil, ya que ahora estoy trabajando con un equipo de desarrolladores experimentados, y a veces mis intentos de escribir código inteligente me parecen tontos, incluso después de que el tiempo disipa la ilusión de la elegancia.

Tu solución yace aquí. Supongo que "experimentado" en este contexto significa "más experimentado que usted". Por lo menos, claramente los respetas. Esta es una valiosa oportunidad de aprendizaje, suponiendo que su ego pueda recibir el golpe. (Cosas molestas, egos. Lástima que las necesitemos así).

¿Tiene revisiones de código con estas personas? Si es así, si aún no lo están haciendo, explícitamente pídales que lo llamen por su mierda. Mencione que ha notado una tendencia en usted mismo a un diseño excesivo, a usar un martillo neumático de primera línea diseñado meticulosamente (preferiblemente manejado por algún tipo de androide automatizado para trabajadores de la carretera) cuando un simple martillo sería más que suficiente .

A menudo puede encontrarse retorciéndose en su asiento mientras su cara se pone roja durante las revisiones del código. Aguántalo. Estás aprendiendo.

Luego, una vez que tenga algunos de estos en su haber, preste atención a los momentos en los que sospecha que posiblemente esté sobrediseñando. Cuando lleguen esos momentos, pregúntese: "Si alguien me llama a esto durante la revisión del código, ¿puedo defender mi solución como la mejor disponible? ¿O hay una solución más simple que estoy abandonando?"

A veces, la revisión por pares es la mejor manera de ver bien su propio trabajo.


Gracias por una muy buena respuesta. No, no tenemos revisiones de código, principalmente porque el proyecto es grande y los recursos del cliente son muy limitados. Pero supongo que puedo ponerme a prueba preguntándome "¿puedo defender mi solución como la mejor disponible?" Al final de cada día.
Dan

77
No hay revisiones de código? Urk Escribiría algo totalmente justiciero y horrorizado, pero también he trabajado en ese entorno. Consumen mucho tiempo y son una molestia para todos, pero realmente son valiosos tanto para el proyecto en cuestión como para su propio desarrollo personal. Si el tema de "¿Tal vez deberíamos estar haciendo revisiones de código?" siempre aparece, asegúrate de que estás abajo como "¡Diablos sí!" Y si no se ven obligados a cumplir con sus propios plazos inminentes, puede pedirles a sus compañeros de trabajo que respete que den un trabajo del que no está seguro acerca de una revisión informal de código.
BlairHippo

1
Bueno, el proyecto es una especie de inicio y, debido a algunos errores de planificación, también en el lado del cliente, enfrentamos la situación cuando realmente necesitamos entregar rápidamente o de lo contrario no vale la pena el esfuerzo. Acabo de hablar con nuestro primer ministro y confirmó que la fecha límite agresiva es la única razón por la que no hacemos revisiones de código, al menos ahora. Si el inicio es exitoso y las limitaciones de tiempo se relajan, es posible que hagamos las revisiones en el futuro.
Dan

2
Oh mi. Suena emocionante, con todas las connotaciones buenas y malas que conlleva la palabra. :-) Buena suerte compañero; Espero que estés al comienzo de algo grandioso.
BlairHippo

8
@BlairHippo: acabo de seguir su consejo, me tranquilicé y le pregunté amablemente al colega que señaló el problema introducido por mis cambios para hacer revisiones informales conmigo, y él estuvo de acuerdo. Esto también ayudó a eliminar cierta incomodidad de nuestra conversación (como en "escribes código complejo y tengo que arreglarlo ..."). ¡Gracias!
Dan

20

Lo mejor es tener en cuenta la máxima de Brian Kernighan:

“La depuración es dos veces más difícil que escribir el código en primer lugar. Por lo tanto, si escribe el código de la manera más inteligente posible, por definición no es lo suficientemente inteligente como para depurarlo ".


1
Estoy totalmente de acuerdo con la cita, pero la pregunta es cómo superar la tentación de ser el niño inteligente . Se le puede decir que no coma helado cuando está enfermo, pero a veces no ayuda.
Dan

13
+1 para una máxima que todo mono de código debería saber de memoria, pero -1 por no ofrecer al OP ninguna idea sobre cómo aplicarlo a su propio trabajo. Por lo tanto, todo se iguala a ningún clic de flecha.
BlairHippo

2
Gran cita, pero en realidad no es una respuesta a la pregunta del OP.
Jim G.

55
Hola Daniel, estamos buscando mucho más que una cita: el sitio solo es útil cuando las preguntas se combinan con respuestas largas y reflexivas llenas de experiencias, hechos y referencias. ¿Hay algo más que, según su propia experiencia, pueda agregar?

2
-1: no responde la pregunta del OP en lo más mínimo.
Thomas Eding

15

Por lo general, existen al menos tres soluciones a problemas de software de alguna importancia: la forma obvia, una forma compleja no obvia (inteligente) y una forma simple no obvia (elegante). Una cita sobre autores es aplicable aquí:

Deja todo lo que se te ocurra y luego eres escritor. Pero un autor es alguien que puede juzgar el valor de sus propias cosas, sin piedad, y destruir la mayor parte. - Colette

No podrá escribir código elegante hasta que pueda juzgar el valor de su propio código, sin piedad, y destruir la mayor parte. Si juzga el código elegante por el resultado final, parece engañosamente fácil, pero requiere reducir la velocidad, revisar muchos borradores, buscar el consejo de otros y eliminar lo que no está en la página. Eso significa que incluso si su código funciona perfectamente, se pregunta a sí mismo o a un colega por qué algo no se siente bien, hasta que esté satisfecho con la respuesta. Tal vez se siente demasiado largo o repetitivo, o siente que el compilador debería haber podido detectar cierto tipo de error. La mayoría de los programadores con una mínima experiencia pueden reconocer código poco elegante fácilmente. El truco es descubrir por qué .

Esa es la forma metódica de escribir código más elegante. También requiere con frecuencia un destello de información que lo ayuda a ver un problema de una manera nueva. Esto es más difícil de lograr, pero ayuda a reducir la velocidad y solo pensar en un problema antes de sumergirse en la codificación. Cuando encuentre una buena solución, busque una mejor. Leer otro código ayuda. Tomar clases o leer libros sobre las mejores prácticas ayuda. Aprender otros paradigmas de programación ayuda. Pedir ayuda a colegas cuyo código admiras ayuda.


3
Eso me recuerda la cita de un viejo matemático: "Para cada problema hay una solución que es simple, elegante e incorrecta".
Joris Timmermans

9

Agregaría a las respuestas existentes, desarrollaría de manera TDD, de modo que primero escriba pruebas sobre lo que debe hacer su código y luego implemente para hacer que sus pruebas se vuelvan ecológicas. De esta manera, solo cumplirá los requisitos que imponen las pruebas. Como escribirás la prueba, es una buena forma de un enfoque autodisciplinado para el desarrollo.


Definitivamente trato de imponerme esto cada vez que hay tiempo.
jnewman

Escribir pruebas después también es una buena manera de detectar grandes errores en su código. De alguna manera se está revisando a sí mismo. Pero TDD es claramente el mejor enfoque si está comenzando de nuevo.
vanna

6

Cuando se trabaja para un equipo grande y dinámico que abarca muchos conjuntos de habilidades y años diferentes, el desarrollo tiene una progresión natural que se "atonta" al nivel más bajo del miembro del equipo más conservador o con mayor deficiencia intelectual, actual o histórico.

Esto puede no ser necesariamente algo malo porque el código inteligente puede ser más difícil de depurar, más difícil de transmitir en una especificación técnica y tomar más tiempo para escribir, lo que ralentiza el tiempo de desarrollo.

Hay momentos en que el código inteligente es importante, como cuando el código inteligente proporciona eficiencia y ganancias de rendimiento más adelante en el ciclo de madurez del software cuando el rendimiento se convierte en un requisito.

El código inteligente también tiene una forma de transmitir un código más rápido de desarrollar y más legible y comprensible a un equipo que puede no estar expuesto a una nueva función de lenguaje o llamada a la biblioteca. Por ejemplo, cuando un desarrollador junior me presentó por primera vez a Linq, tuve un disgusto inmediato por ser innecesario, difícil de depurar, tonto e "inteligente". Después de jugar con él y descubrir cuán útiles y poderosas pueden ser las consultas de Linq, invertí el tiempo para aprenderlo y mi código DAL nunca ha sido más limpio y legible, así como más fácil de depurar y extender.

Lamento no haber tenido una mente abierta antes y desearía no haber sido tan duro con un desarrollador junior tan "inteligente".

Mi punto es que el código "inteligente" DEBE ser sospechoso, pero no deberíamos ir a una cruzada contra él porque puede sofocar la creatividad y la innovación.

EDITAR: Me acabo de dar cuenta de que no respondí completamente a tu pregunta. Si tiene la capacidad en su proyecto para escribir fácilmente un código inteligente, entonces quizás el equipo debería adoptar estándares de codificación más estrictos para seguir una plantilla y un estilo uniformes y distintos. Esto ayudará a dibujar las líneas de su caja de arena para que no se pasee por la calle persiguiendo una pelota.


6

Si el 20% (su% puede variar) o más de sus líneas agregadas necesitan documentación, es hora de retroceder y repensar .

Realmente creo que debes esforzarte por ser inteligente, es un efecto secundario natural ser más competente. Darse una pauta general como el% de comentarios necesarios para aclararse es una buena manera de obligarse a retroceder y evaluar si usar esa cosa nueva que aprendió es una buena elección o simplemente una forma de mostrar su nuevo juguete.


3
Tiendo a considerar la documentación / comentarios como un fracaso. Cuando necesita documentar / comentar algo, significa que, en primer lugar, su código no está claro. Desafortunadamente, este es un objetivo poco realista y necesitamos una documentación en algún momento. Solo tenga en cuenta que esta parte del código debe reducirse a una cantidad mínima.
deadalnix

@deadalnix: No es un mal punto. Sospecho que mi% sería más alto que la mayoría porque usualmente codifico en un lenguaje ensamblador que de otra manera estaría muerto y muy macro. Es más difícil de leer y cada nuevo empleado tiene que aprender el idioma, como resultado se requieren más comentarios.
DKnight

2
@deadalnix: documentación para explicar cómo es una señal de que su código no está claro. Documentación para explicar por qué es muy necesaria. He visto demasiadas piezas de código que pude entender lo que hicieron, pero no por qué decidieron hacerlo de una manera no intuitiva. Eso hace que sea muy difícil de mantener.
HLGEM

@HLGEM Esto es discutible. La falta de claridad del código puede provenir de libs / API mal diseñados, de la falta de claridad dentro de la concepción misma, como una mala separación de las preocupaciones. Vivimos en el mundo real y nuestras capacidades son finitas, por lo que definitivamente necesitamos documentación, pero cada vez que la necesitamos, significa que alguien ha escrito un código incorrecto. No hay documentación que no sea algo que deba hacer, ni siquiera piense en ello, sino algo en lo que tiene que pensar todo el tiempo para seguir mejorando en la dirección correcta.
deadalnix

@deadalnix: el código perfecto nunca es una solución práctica en el mundo real.
JeffO

4

No puedo resistirme a intentar algo inteligente.

Entonces lo hago en un proyecto de juguete, en mi propio tiempo, en casa.

Cuando la novedad desaparece, el problema está resuelto.


3

Creo que una forma de averiguar si su código es demasiado "inteligente" es dar un paso atrás y preguntarse lo siguiente:

Si tuviera que dar una copia impresa de este código a alguien que nunca ha trabajado en este proyecto / código, ¿podrían leerlo y describirme qué hace la función (después de darles un breve contexto)? Si no es así, ¿qué cantidad de explicaciones tendría que hacer? ¿Cómo le explicaría esto a alguien que toma CS101?

Si resulta que tendrías que guiar a alguien a través de cada línea o la mayoría de las líneas en un método o clase, probablemente sea demasiado inteligente. Si tiene que explicar construcciones de lenguaje (LINQ por ejemplo) a alguien que no está familiarizado con él, probablemente esté bien. Si tiene que mirar una línea y pensar un poco antes de poder explicarla, su código debe ser refactorizado.


Escuché esto llamado "Duck de goma" cuando se aplica a la resolución de problemas; cuando esté perplejo, intente explicar el problema a alguien que no sepa nada al respecto (como, ya sabe, su patito de goma) y vea si la solución no cae en su regazo. Tengo que pensar que funcionaría para esto también.
BlairHippo

2

1) Quemarse antes para que sepas que es algo malo. Intentar depurar algo de hace mucho tiempo que está escrito inteligentemente es muy divertido. Creo que tienes eso cubierto.
2) Comente su código, explique lo que está haciendo antes de cada sección de código.
3) Si tiene dificultades para explicarlo o siente la necesidad de insertar un diagrama, entonces lo que acaba de hacer es demasiado inteligente y probablemente podría hacerse de manera más limpia.

Las soluciones inteligentes a los problemas pueden ser fantásticas, hasta que tenga que depurarlas o expandirlas. A veces es la única solución. Si puede describir con precisión qué demonios hace y cómo lo hace, las soluciones inteligentes pueden ser aceptables.

Usualmente uso los comentarios para describir lo que estoy haciendo con una sección de código. Si parece lo menos confuso, también describo cómo lo estoy haciendo. Idealmente, el código debe ser sencillo y explicarse por sí mismo. Pero si me cuesta explicar cómo hice lo que acabo de hacer, entonces es una clara señal de que necesito dar un paso atrás e intentarlo de nuevo.


2
El truco de comentarios también funciona para mí. Entre otras razones, siempre incluyo un bloque de comentarios sobre cualquier subrutina no trivial como una especie de verificación de cordura final. Si encuentro que tengo que explicar mucho (o incluso, en ocasiones, disculparme) secciones de código enrevesadas u obtusas o parámetros de entrada extraños o lo que sea, es una señal de advertencia que podría necesitar repensar un poco la solución.
BlairHippo

@BlairHippo HA! "verificación de cordura final" Me gusta eso.
Philip

2

Probablemente, una buena manera de comenzar a escribir código simple es liberar la pasión por la inteligencia en un proyecto que pide inteligencia . El resto de la respuesta es específica de .NET, pero estoy seguro de que se pueden encontrar proyectos de nivel similar en cualquier otro idioma.

Hay marcos de inyección de dependencia de código abierto para trabajar en los que solo se piden Expressionconocimientos de trucos, hay F # y una maravillosa gama de tareas para las que uno puede probar.

Si te interesan las matemáticas (y eso es independiente del lenguaje ), existe el Proyecto Euler para ti.

Por último, pero no menos importante, en el mundo .NET hay Mono Project que tiene muchas áreas que necesitan atención del desarrollador , algunas de ellas bastante complicadas. ¿Qué tal contribuir a una herramienta de análisis de código estático de código abierto .NET ? Hay algunos análisis de IL involucrados, así como cosas de alto nivel. Jb Evain siempre trabaja en algo interesante, ya sea la biblioteca de reflexión Cecil, el Expressionsoporte o un descompilador .NET.

Si nada encaja, simplemente inicie su propio marco de burla :-)


2

¿Conoces esa sensación cuando solo necesitas presumir ese nuevo truco con Expresiones o generalizar tres procedimientos diferentes?

No

Esta es una de las razones por las que siempre digo que es bueno cuando los nuevos desarrolladores se ven envueltos en un gran lío de código de speghetti indocumentado para mantener y refactorizar. Les enseñará las realidades de mantener un código demasiado 'inteligente' que no escribieron, y con suerte inculcará algo de empatía por el pobre imbécil que tendrá que depurar su código dentro de 5 años.


Creo que es más probable que esto los haga sentir frustrados y creo que SU código será mucho mejor y más elegante que los novatos que escribieron ESTE desastre. Nadie escribe código con la intención de dificultar el mantenimiento.
Sara

2

Creo que el tema está bien elegido. Es "genial" escribir una línea de Perl que hace diez mil cosas a la vez, pero luego apesta cuando tienes que volver a visitarla.

En una nota diferente, inteligente o no, el código debe documentarse. Existe un desajuste inherente de impedancia entre los lenguajes de programación aceptados por la industria y los conceptos de alto nivel a los que nosotros, como humanos, estamos acostumbrados en nuestro pensamiento. El código autodocumentado simplemente no es realizable, hasta que se convierte en lenguaje natural. Incluso el código Prolog necesita ser documentado, ya que, por alto que sea, sigue siendo bastante formal.

El código imperativo de grano fino sirve para implementar planes de grano grueso; eso debe documentarse. No quiero tener que leer las 50 líneas del método cuando un comentario rápido de una hoja de ruta de 3 líneas servirá.

Edición posterior: un ejemplo más elocuente es el que trasciende las computadoras. Un libro puede estar muy bien escrito, pero a menudo queremos procesarlo en diferentes niveles de abstracción. A menudo, un resumen del libro servirá, y eso es lo que los comentarios pueden ofrecer al código. Por supuesto, un código bien abstraído puede recorrer un largo camino hacia la auto documentación, pero no puede brindarle todos los niveles de abstracción.

Y los comentarios también pueden actuar como notas al margen en un libro, cuando necesitamos explicar el proceso de razonamiento detrás de un reclamo en el texto principal sin descarrilarlo.

Con este contexto, encuentro que mi afirmación previa referente al lenguaje natural que trasciende la necesidad de comentarios es incorrecta. Incluso el lenguaje natural, como en un libro, puede prestarse a la documentación, para explicar de manera dispersa la abstracción incorporada en el texto, o para proporcionar desvíos sin descarrilar el texto principal. Con la nota de que el código bien resumido ya puede haber recorrido un largo camino hacia la autodocumentación.

Por último, pero no menos importante, los comentarios pueden ayudar al codificador a mantener un alto nivel de abstracción. Muchas veces me doy cuenta de que dos comentarios consecutivos que incluí en una lista de pasos no hablan al mismo nivel de abstracción, lo que inmediatamente garantiza una mirada crítica de lo que estoy haciendo con ese código.

Ciertos problemas trascienden la codificación y afectan la codificación al igual que otras actividades. Los comentarios pueden proporcionar esa ayuda para aclarar los fundamentos y las facetas de nuestro código, y les encuentro un compañero agradable que habla un idioma más suave para beneficiar a la persona por un cambio.


1

¿Cómo? Sigue mostrando tu código a esos desarrolladores experimentados. y cuando te critiquen por ser sofisticado y llamativo, sorpréndelo y pregúntales cómo lo harían y por qué (de una manera no conflictiva, por supuesto).

Editar a la luz de -1:

Hace muchas lunas, estaba en la misma situación: tenía un jefe que se encogía cada vez que usaba un puntero en Delphi o 'con constructo', otro que amenazaba con despedirme si no dejaba de hacer un cortocircuito en todos mis booleanos. con 0-1 y usando variables de una sola letra en todas partes.

Aprendí porque pregunté por qué y se tomaron la molestia de explicar porque pensaron que podría llegar a algo: jajaja ...


1
Hola Mikey, estamos buscando mucho más que una frase: el sitio solo es útil cuando las preguntas se combinan con respuestas largas y reflexivas llenas de experiencias, hechos y referencias. ¿Hay algo más que, según su propia experiencia, pueda agregar?

1

¿Siento la necesidad de presumir? No, no más. ¿Cómo lo supere? Como la mayoría de las personas superan cualquier otro mal hábito ... la práctica consciente y deliberada de las técnicas adecuadas. Si lo hace lo suficiente, comprenderá el valor de las mejores prácticas y, mediante su uso constante, desarrollará buenos hábitos.

También tenga en cuenta que al centrarse en el software funcional, que es puntual y fácil de mantener, obtendrá el reconocimiento que busca. Los desarrolladores experimentados se acercarán a usted y le dirán: "El módulo que escribió está bien diseñado. Solo tuve que implementar un componente para conectarlo a mi proyecto". en lugar de "¿Tuve que reelaborar todo el módulo que escribiste solo para usarlo en otro componente? ¿Has oído hablar de Bob Martin o Ward Cunningham?"

TLDR: no estás solo. El reconocimiento de la habilidad se logra mejor como un subproducto de la resolución de problemas de manera inteligente.


0

Para mí, el código demasiado inteligente a menudo se esfuerza por resolver requisitos futuros imaginarios en lugar de centrarse en los requisitos de hoy. Gran trampa!

0% de código demasiado complicado no es un objetivo alcanzable. Quizás ni siquiera el mejor objetivo por el que luchar. El código demasiado complicado es malo, pero debes probar cosas nuevas para crecer como programador. No debe probarlos en el código de producción si puede evitarlo. A diferencia de las máquinas, los humanos cometen errores.

Las revisiones de código ayudan. Pasar años arreglando el código "inteligente" de otras personas ayuda. Mantenerse enfocado en lo que el cliente realmente necesita hoy ayuda.

Las escuelas y las empresas cuentan con equipos de personal de limpieza y mantenimiento. ¡El código también necesita limpieza y mantenimiento! ¡Cuando sea posible, limpia el desastre (especialmente el tuyo)! Creo que es lo mejor que se puede hacer.


-2

Además de los buenos consejos dados hasta ahora (revisión de código, depuración, enfoque TDD), debe (re) leer periódicamente (los mejores libros en mi opinión) sobre buenas prácticas de codificación:

  • Programador pragmático
  • Código completo
  • Código limpio

y otros, dependiendo de la tecnología que use.


-2

Solo recuerda YAGNI: no lo vas a necesitar .

el programador no debe agregar funcionalidad hasta que se considere necesario ...

YAGNI es un principio detrás de la práctica de XP de "hacer lo más simple que pueda funcionar" (DTSTTCPW). Está destinado a usarse en combinación con varias otras prácticas, como la refactorización continua, las pruebas unitarias automáticas continuas y la integración continua. Utilizado sin refactorización continua, podría generar código desordenado y un trabajo masivo ...

Según quienes abogan por el enfoque YAGNI, la tentación de escribir código que no es necesario en este momento, pero podría serlo en el futuro, tiene las siguientes desventajas:

  • El tiempo empleado se toma agregando, probando o mejorando la funcionalidad necesaria.
  • Las nuevas funciones se deben depurar, documentar y admitir.
  • Cualquier característica nueva impone restricciones sobre lo que se puede hacer en el futuro, por lo que una característica innecesaria puede impedir que se agreguen características necesarias en el futuro.
  • Hasta que la característica sea realmente necesaria, es difícil definir completamente lo que debe hacer y probarla. Si la nueva función no se define y prueba correctamente, es posible que no funcione correctamente, incluso si finalmente se necesita.
  • Conduce a la hinchazón de código; El software se hace más grande y más complicado.
  • A menos que haya especificaciones y algún tipo de control de revisión, la función puede no ser conocida por los programadores que podrían utilizarla.
  • Agregar la nueva función puede sugerir otras funciones nuevas. Si estas nuevas funciones también se implementan, esto puede dar como resultado un efecto de bola de nieve hacia el arrastre de la función ...

3
Si bien esto puede ser cierto, más detalles harían de esta una respuesta mucho mejor.
ChrisF
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.