Durante mi aprendizaje en el desarrollo de contratos inteligentes, así como en el desarrollo en general, me he dado cuenta de algo muy importante: un código bien escrito es de gran ayuda para los futuros desarrolladores. Un ejemplo que me encanta es el código de Doom (1933), que destaca por su simplicidad y facilidad de comprensión.
En el desarrollo de contratos inteligentes la legibilidad y claridad de tu código no solo ayuda a que futuras personas que quieran estudiarlo puedan comprender este, también es una gran herramienta para auditores, ya que ellos pueden analizar el contrato de manera más eficiente, identificar posibles vulnerabilidades y asegurar que cumpla con los estándares de seguridad requeridos.
Aunque hay una persona muy importante a la que tu código ayudará más que a nadie en el momento y es a ti mismo.
Tal vez te ha pasado que después de horas y horas de desarrollo, lo dejes para mañana y en el momento que regresas a tu computadora simplemente te quedas en blanco. ¿Para qué servía esta función? ¿Qué hice aquí? ¿Qué diablos significa esperoQueFuncione
? Estas son solo algunas de las preguntas que pueden surgir cuando el código no está bien escrito (y también no documentado).
Esto se debe a que como devs somos propensos a olvidar detalles importantes a medida que trabajamos en proyectos, nuevas funciones o nos centramos en nuevas tareas. Y es aquí donde el escribir bien tu código sera un gran salvavidas no solo para ti mismo, también para futuros desarrolladores que quieran implementar sobre tu código e incluso un gran salvavidas para tu protocolo.
Por todo esto, invertir tiempo en escribir un código limpio y bien documentado es una práctica esencial en el desarrollo de software. No solo facilita tu propio trabajo y el de otros desarrolladores, sino que también contribuye a la sostenibilidad y evolución exitosa del proyecto en general. Así que, como desarrolladores, es importante recordar que el código que escribimos no solo impacta el presente, sino también el futuro del desarrollo de software.
Existen varias maneras en las cuales puedes mantener un código más limpio y legible, pero en este caso nos enfocaremos en algunas prácticas básicas de higiene en la escritura de código en Solidity.
Uno de los errores más comunes que pueden ocurrir es tener una indentación incorrecta en nuestro código. Esto puede llevar a confusiones, especialmente al trabajar con funciones anidadas. La documentación de Solidity nos especifica que nuestra indentación debe ser de 4 espacios por nivel.
Por motivos practicos usaremos “•“ para reprecentar un espacio
Incorrecto
contract A{
••uint256 algunaVariable;
•••if (algunaVariable == 1){
••••...
•••}
}
Correcto
contract A{
••••uint256 algunaVariable;
••••if (algunaVariable == 1){
••••••••...
••••}
}
Aunque muchos editores de texto por defecto ya realizan este tipo de indentación, no está de más que revisemos de manera manual si esta está correcta.
Tal vez una de las partes más discutidas no solo en Solidity, sino también en cualquier lenguaje de programación, es la declaración de variables. Es un tema tan controvertido que muchos que lean esto estarán de acuerdo conmigo, o de plano dirán que su manera es mejor que esta.
En este caso, quiero hacer referencia tanto a la documentación oficial de Solidity como el Solidity Style Guide del repositorio de Chainlink.
Nos enfocaremos en las principales, pero si quieres ver más, en la bibliografía dejaré la documentación, así como el Solidity Style Guide.
Los nombres de los contratos deben comenzar en mayúsculas y se debe evitar el uso de guiones y guiones bajos.
//Correcto
contract ContratoDeEjemplo {
...
}
//Incorrecto
contract contratoDeEjemplo {
...
}
contract contrato_De_Ejemplo {
...
}
contract Contrato_De_Ejemplo {
...
}
Dependiendo del tipo de variable, la norma básica es que los nombres de las variables comiencen con minúscula y se utilicen mayúsculas para denotar una nueva palabra, como por ejemplo variableDeEjemplo
. Sin embargo, cuando almacenamos una variable en la blockchain, debemos usar el prefijo s_
al principio, por ejemplo, s_algunDato
. Además, si la variable es del tipo "inmutable", debe llevar el prefijo i_
al inicio. La única excepción donde podemos usar mayúsculas y guiones bajos es en el uso de variables constantes.
Quizás pueda sonar ilógico, especialmente en el caso de utilizar s_
, pero permíteme darte un ejemplo que experimenté.
En un curso, se explicó que las variables de tipo array son de las más costosas en términos de gas. Sin embargo, hay un pequeño truco que puede ayudar a ser más eficiente en su uso, y es el uso de memory
. Imagina que son las 3 de la mañana y sigues trabajando en un contrato. Por error, en lugar de usar un array en memory, lo usas almacenado directamente en la blockchain. No habrá errores, pero será menos eficiente en términos de gas. Ahora, ¿qué pasaría si tuvieras el sufijo s_
al principio? Quizás con ello te darías cuenta de que es una variable almacenada en la blockchain y, para ahorrar gas, decidirías pasarla a una variable en memory, para luego devolverla a la variable original, ahorrando una considerable cantidad de gas y, en el proceso, haciendo que el cliente ahorre dinero.
Una de las reglas personales al escribir código es marcar primero el contrato del cual proviene un error y, luego, mediante guiones bajos, explicar el error. Se debe evitar, en la medida de lo posible, utilizar strings debido al ahorro en gas. Por ejemplo, podríamos tener una función llamada ContratoDeDefi___NoSePudoDevolver();
.
El mantener tus variables bien escritas y descriptivas especialmente en las funciones, proporcionan un contexto más claro que un comentario. Tomemos un ejemplo, ¿podrías decirme, sin trampa, para qué sirve esta función?
contract votacion {
address lista[];
function ver(uint256 a) public view returns(address) {
return s_lista[a];
}
}
tenemos que es un contrato para una votación y un arreglo de una lista, pero podría ser cualquier cosa, podría ser para las direcciones de los candidatos o del partido, podría ser una lista para tener en cuenta los que votaran en cierto lugar.
Ahora pongamos algo de contexto y higiene a ese contrato
contract Votacion {
address[] private s_listaDeCandidatos;
function obtenerCandidato(
uint256 indice
) public view returns (address addressCandidato) {
return listaDeCandidatos[indice];
}
}
ahora tenemos bien el contexto el cual es un contrato para un sistema de votación. La función obtenerCandidato
se utiliza para obtener la dirección del candidato en un índice específico en el arreglo listaDeCandidatos
.
El escribir variables, funciones y contratos mejor explicados pueden ser mas poderosos que cualquier tipo de comentario dentro del contrato mismo. Y hablando de comentarios…
Llamado por sus siglas, el Ethereum Natural Language Specification Format es una manera de comentar nuestros contratos de forma más eficiente. Esto se realiza de dos maneras: en una sola línea usando ///
o en múltiples líneas usando /** */
. Esta es una herramienta esencial al escribir nuestros contratos, ya que puede proporcionar más contexto y explicar funciones de una manera más profunda. Podemos incluir información sobre quién lo escribió, las funciones involucradas e incluso explicar la entrada y salida de ciertas funciones.
No entraremos de lleno a estos, pero puedes ver ejemplos muy buenos en los contratos de Aave y Uniswap.
El principio DRY (Don't Repeat Yourself) o también conocido como DIE (Duplication is Evil) es una guía fundamental en el desarrollo de software que promueve la eficiencia y la legibilidad del código. Este principio se resume en la siguiente afirmación: "Cada pieza de conocimiento debe tener una única, inequívoca y autoritativa representación dentro de un sistema".
En otras palabras, DRY sugiere que el código no debe contener duplicación innecesaria. Si hay una funcionalidad o información que se repite en diferentes partes del sistema, debería ser abstracta y encapsulada en un lugar centralizado. Al evitar la duplicación, logramos varios beneficios:
Mantenimiento simplificado: Al tener una única fuente de verdad para una funcionalidad o dato, cualquier cambio o actualización solo necesita realizarse en un lugar. Esto minimiza la posibilidad de errores y facilita el mantenimiento a largo plazo.
Reducción del tamaño del código: La eliminación de duplicaciones reduce la cantidad de líneas de código, lo que mejora la legibilidad y reduce la complejidad general del sistema.
Consistencia: Al utilizar una única representación, aseguramos que todas las partes del sistema accedan a la misma información, evitando inconsistencias y resultados inesperados.
Facilidad de comprensión: Con menos duplicación, el código se vuelve más claro y conciso, lo que facilita su comprensión tanto para el desarrollador original como para otros colaboradores.
Reusabilidad: Al encapsular la lógica en una ubicación central, es más fácil reutilizarla en diferentes partes del sistema o en futuros proyectos.
En el contexto de los contratos inteligentes en Solidity, aplicar el principio DRY implica evitar repeticiones innecesarias de código. Por ejemplo, si hay una función que realiza una operación específica, en lugar de duplicar esa función en diferentes partes del contrato, sería mejor definirla una vez y reutilizarla donde sea necesario.
Mantén tu código absolutamente simple. Sigue revisando tus funciones y busca cómo simplificarlas aún más.
- John Romero (2016 GDC Europe)
Al seguir el principio DRY, se promueve un diseño más eficiente, mantenible y escalable en los contratos inteligentes, lo que mejora la calidad general del desarrollo y la confiabilidad del sistema.
Si bien estos consejos te ayudaran a tener un código mas legible, no significan que sean los diez mandamientos de la escritura en contratos, cada persona tenemos una manera de escribir codigo y eso esta bien, en palabras de John Romero
La programación es una forma de arte creativa basada en la lógica. Cada programador es diferente y codificará de manera distinta. Lo que importa es el resultado final.
- John Romero (2016 GDC Europe)
Aun así, es una buena práctica tener ese tipo de higiene, ya que hará más fácil tu trabajo y el de los demás. En palabras de Romero.
Intenta programar de manera transparente. Informa a tu líder y compañeros exactamente cómo vas a resolver tu tarea actual y recibe comentarios y consejos. No trates la programación como si cada programador fuera una caja negra. El proyecto podría salirse de los rieles y causar retrasos.
- John Romero (2016 GDC Europe)
Guzel, B. (2021). Top 18 Best practices for writing super Readable code. Web Design Envato Tuts+. https://webdesign.tutsplus.com/top-15-best-practices-for-writing-super-readable-code--net-8118t
Smartcontractkit. (n.d.). chainlink/contracts/STYLE.md at develop · smartcontractkit/chainlink. GitHub. https://github.com/smartcontractkit/chainlink/blob/develop/contracts/STYLE.md
Solidity — Solidity 0.8.21 documentation. (n.d.). https://docs.soliditylang.org/en/v0.8.21/