Lo que me hubiera gustado saber
antes de trabajar
como programador
en la industria
del desarrollo
de software
1. Ser un muy buen programador no es cuestión de genios
Practicar,
Practicar,
Practicar
20 Horas para pasar
la "molestia" inicial,
10.000 Horas
para ser "Experto"
"No soy un muy
buen programador.
Soy simplemente un
buen programador con muy buenos hábitos"
-- Kent Beck
"No soy un muy
buen programador.
Simplemente leo mucho"
-- Yuji Kiriki
¿Qué leer?
- Libros
- Conferencias: Devoxx,
Java One, etc. (YouTube) - Blogs (Twitter)
- Java Magazine
¡Aprende un
nuevo lenguaje!
2. Aprende la
cultura del
lenguaje que usas
Para Hacktoberfest decidí colaborar en: iiitv/algos
Problema de las N-Reinas
private static boolean solveNQueen(int[][] board, int column) {
int boardLength = board[0].length;
if (column >= boardLength)
return true;
for (int i = 0; i < boardLength; ++i) {
if (isQueenSafe(board, i, column)) {
board[i][column] = 1;
if (solveNQueen(board, column + 1))
return true;
else
board[i][column] = 0;
}
}
return false;
}
Lo que había antes
private static boolean isQueenSafe(int[][] board, int row, int column) {
int i;
int j;
int boardLength = board[0].length;
for (i = 0; i < column; ++i)
if (board[row][i] == 1)
return false;
for (i = row, j = column; i >= 0 && j >= 0; --i, --j)
if (board[i][j] == 1)
return false;
for (i = row, j = column; j >= 0 && i < boardLength; ++i, --j)
if (board[i][j]==1)
return false;
return true;
}
Lo que había antes
public static void main(String[] args) {
int boardLength = 8;
int[][] board = new int[boardLength][boardLength];
if (!solveNQueen(board, 0)) {
System.out.println("No solution");
} else {
for (int i = 0; i < boardLength; ++i) {
System.out.println(Arrays.toString(board[i]));
}
}
}
Lo que había antes
¿En qué lenguaje estaba escrito ese código?
"Reconozco al león por su pisada" -- Isaac Newton
La mayoría podía estar
escrito en C o cualquiera
de sus derivados
Cuando Java no es Java
- No hay tipos de datos
- No hay uso de toString()
- No hay uso de interfaces
bases como Iterable
public static void main(String[] args) {
NQueenProblem queens = new NQueenProblem(5);
for (Solution s : queens) {
System.out.println(s);
}
}
public static class Solution {
private final List<Integer> board;
private static char QUEEN = 'Q';
private static char EMPTY_SQUARE = '.';
private static String NEW_LINE = String.format("%n");
private Solution(final List<Integer> board) {
this.board = board;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < board.size(); ++i) {
for (int j = 0; j < board.size(); ++j) {
if (board.get(i) == j) {
builder.append(QUEEN);
} else {
builder.append(EMPTY_SQUARE);
}
}
builder.append(NEW_LINE);
}
return builder.toString();
}
}
private class NQueenIterator implements Iterator<Solution> {
// más código
@Override
public boolean hasNext() {
while (!queue.isEmpty()) {
List<Integer> current = queue.removeLast();
if (current.size() == size) {
this.next = new Solution(current);
return true;
}
int column = current.size();
for (int row = 0; row < size; ++row) {
if (isValid(current, column, row)) {
List<Integer> next = new ArrayList<>(current);
next.add(row);
queue.addLast(next);
}
}
}
return false;
}
@Override
public Solution next() {
return next;
}
}
public class NQueenProblem
implements Iterable<NQueenProblem.Solution> {
private final int size;
public NQueenProblem(final int n) {
this.size = n;
}
@Override
public Iterator<Solution> iterator() {
return new NQueenIterator();
}
public static class Solution {
// implementación
}
private class NQueenIterator
implements Iterator<Solution> {
// implementación
}
}
No solo se trata de escribir código que funcione,
es bueno entender lo
que busca el lenguaje
¡En Internet hay muchísimo código que no es Java!
No porque esté en
Internet, o en un curso
(pago o gratuito) significa que sea buen código
Libros
- Effective Java
- Clean Code
- Head First Design Patterns
¡Parte de la cultura
de un lenguaje son
sus últimas versiones!
Trata de aprenderlas y estar pendiente de las discusiones sobre ellas
¡Esto aplica también
a las herramientas, FW
o librerías que sean
parte de tus proyectos!
3. Tipos de Datos
¡La programación
debería tratar de hacer nuevos tipos de datos!
Tipos de Datos
- Son más expresivos
- Tienden a tener menos errores
- Hacen que ciertos errores sean imposibles
- Hace el código más fácil de probar
public void execute(String query)
public void execute(Query query)
En vez de
usar
Una vez que la idea
de Tipos de Datos
es clara, todos los demás conceptos son más sencillos de entender
p.j. Clases Internas
Solution y NQueenIterator
son clases
internas de NQueen
Clases Internas
- Tienen acceso al estado de la
clase externa (p.j. Builder) - Se puede tener control para
que solo la clase externa cree
objetos de la clase interna (p.j. Solution)
Aprende todos
las características
del lenguaje que usas
Cuando vayas a hacer un diseño, necesitas conocer la herramienta idónea
"Si la única herramienta que tienes es un martillo,
es tentador tratar todo como una puntilla."
-- Abraham Maslow
4. Aprendiendo
a Aprender
¿Cómo pasar un
exámen difícil?
- Hago un esfuerzo todos los días
leyendo el material escrito - Me trasnocho el día anterior
para tener todo más freso - Estudio varios días,
sin transnocharme
"Modos" del cerebro
- Focused
- Difussed
Focused
- Como su nombre implica,
estamos concentrados aprendiendo - El cerebro está muy activo,
así que los pensamientos tienen
a relacionarse con conceptos familiares - Es muy difícil tener patrones innovadores
Diffused
- Típicamente sucede cuando
no estamos pensando
activamente en un problema - Hay "más espacio" para que los
conceptos se asocien con nuevos conceptos,
lo que facilita aprender nuevas cosas - Es una manera de ver "la gran imagen"
El cerebro solo puede
estar en un modo
Para aprender,
la mejor manera es
ir de un modo a otro
Sin descansar, el cerebro
no puede estar en modo diffused. ¡La repetición espacial es la clave!
La primera vez que
trato de aprender sobre
un tema complejo,
usualmente,
no entiendo nada.
Ni la segunda,
ni la tercera,
ni la cuarta...
Y usualmente un día sin estar pensando en eso, todo hace "clic"
"No soy un muy buen programador. Solo un
buen programador con muy buenos hábitos"
20 horas para pasar
la molestia inicial
del aprendizaje
Encuentras difícil concentrarte para
hacer una tarea
¿Cómo lo manejas?
- Me obligo a iniciar la tarea, con la esperanza de que después de arrancar, el tedio se vaya
- Simplemente te paras y vuelves en una hora para tener una perspectiva nueva
- Simplemente te paras y vuelves cuando te sientas listo para continuar
Cuando haces algo
que no quieres, el cerebro lo asocia con "dolor"
Pero cuando arrancas
a hacerlo, el cerebro
se siente intrigado por
la tarea y la sensación
de desagrado se va
Para vencer la procrastinación una opción es la técnica Pomodoro
Para crear hábitos
de aprendizaje
- Duerme bien
- Haz ejercicio
- Únete a un grupo de estudio
Para aprender, es muy importante usar el modo diffused del cerebro
Para aprender, es
muy importante tener
una buena oxigenación
en el cuerpo
Para aprender, es
muy importante activar para zonas del cerebro
Para aprender mejor: duerme bien, haz ejercicio y estudia con otros
¡También toma notas
a mano y dibuja!
5. NULL
¿La mejor forma de evitar NullPointerException?
Solo usa NULL como un detalle de implementación
"Soluciones" para NULL
- @NonNull y @Nullable
- Nullable Type: T?, T!, etc.
- Elvis operator ?:
¡Todas son curitas para controlar una hemorragia!
No existe ningún código que dependa de usar NULL
Siempre hay una alternativa mejor que usar NULL
"Mi experiencia es que
si sigues una regla
estricta de nunca retornar null, todo tu código se vuelve más limpio y seguro para los usuarios"
-- Stephen Colebourne
Nunca uses NULL
6. Inmutabilidad
Las clases que
diseñes deberían ser inmutables por defecto
Entender un proceso
que usa objetos
inmutables es
mucho más sencillo
Tener objetos inmutables elimina un montón de errores en los programas
Escribir una clase inmutable es un "poco" más intricado, pero no tanto
7. ¿De quién es
la responsabilidad
de XXX?
¡TODOS!
Nada en mi proyecto es el problema de alguien más
Nada en mi compañía es el problema de alguien más
¡No te dejes etiquetar, ve más allá de los roles!
Empowerment
- Cuando hay un problema, ayuda
- Cuando hay código que mejorar, propon
- Cuando hay un proceso que no funciona, compártelo
"¡No me traigas problemas, tráeme soluciones!" -- Frances Frei
Aún más, el mundo
ha cambiado:
https://www.infoq.com/news/2017/08/volkswagen-engineer-sentence
8. Código que
mi abuela
pueda entender
Clean Code es un mantra
¿Se puede escribir código que entienda alguien
que no programa?
public CribaDeEratóstenes() {
destacharTodosLosNúmeros();
tacharNúmerosObvios();
tacharMúltiplosDePrimosConocidos();
}
public boolean esPrimo(int n) {
return noEstáTachado(n);
}
Criba de Eratóstenes
private void tacharNúmerosObvios() {
tachar(0);
tachar(1);
}
private void tacharMúltiplosDePrimosConocidos() {
for (int n = 2;
menorMúltiploPosiblementeNoTachado(n) <= MAX_PRIMO; ++n) {
if (esPrimo(n)) {
tacharMúltiplosDe(n);
}
}
}
private void tacharMúltiplosDe(int n) {
for (int x = menorMúltiploPosiblementeNoTachado(n);
x <= MAX_PRIMO; x = siguienteMúltiplo(x, n)) {
tachar(x);
}
}
private void heapify() {
int index = size;
while (hasParent(index)
&& greater(parentIndex(index)), index) {
swap(parentIndex(index), index);
index = parentIndex(index);
}
}
}
Min Heap
private void makeHeap() {
swap(0, size);
int index = 0;
while (hasLeftChild(index)) {
int minChildIndex = getMinChildIndex(index);
if (less(parentIndex(index), minChildIndex) {
return;
}
swap(parentIndex(index), minChildIndex);
index = minChildIndex;
}
}
Procura que los
métodos públicos
se lean como prosa
Si los métodos se leen como prosa, seguramente son métodos cortos
Métodos Cortos
- Más fáciles de entender
- Menos errores
- Más fáciles de probar
Escribir código limpio
es un proceso iterativo
Escribir código
- Haz que funcione
- Haz que sea limpio
- Haz que sea rápido
La ley del Boy Scout:
"Trata de dejar este
mundo un poco mejor
de lo que lo encontraste."
-- Robert Stephenson
La ley del Boy Scout:
"Has push de código más limpio de como estaba cuando hiciste pull."
9. Sacar la
cabeza de la arena
Requerimiento #1:
Si un texto tiene más
de 210 caracteres, se debe cortar y agregar "..."
public static String ellipsify(String s) {
if (s.length() < 210) {
return s;
}
return s.subtring(0, 210) + "...";
}
¿No deberíamos ser más amigables y no cortar en medio de una palabra?
Requerimiento #2:
El método ellipsify coloca los puntos suspensivos después del primer espacio después del caracter 210
public static String ellipsify(String s) {
if (s.length() < 210) {
return s;
}
int cut = s.indexOf(' ', 210);
if (cut == -1 || cut == s.length() - 1) {
return s;
}
return s.subtring(0, cut) + "...";
}
Requerimiento #3:
El argumento de ellipsify puede incluir hipertexto que no debe ser contado como caractetes
Requerimiento #4:
El argumento de ellipsify puede incluir caracteres que no debe ser contado como caractetes
¡Pensar en índices
puede llevar el código
a volverse empalagoso!
Pero los programadores usualmente piensan así.
¡Hay que sacar la
cabeza de la arena!
¡La manera funcional
de programar!
Pensemos en partir
el requerimiento en más
de una función de uso específico y componerlas para crear nuestra funcionalidad
public final class WordSplitter {
private static final
Predicate<Character> IS_WHITESPACE = Character::isWhitespace;
private static final
Predicate<Character> IS_NOT_WHITESPACE = isWhitespace.negate();
private final String text;
private Predicate<Character> predicate;
private int i = 0;
public WordSplitter(final String text) {
this.text = java.util.Objects.requireNonNull(text);
this.predicate = isNotWhitespace.test(text.charAt(0))
? isNotWhitespace
: isWhitespace;
}
// más código
}
public final class WordSplitter {
// más código
public boolean hasNext() {
return i < text.length();
}
public String next() {
int end = next(i);
String chunck = text.substring(i, end);
i = end;
predicate = (predicate == IS_WHITESPACE
? IS_NOT_WHITESPACE
: IS_WHITESPACE);
return chunck;
}
private int next(int start) {
while (start < text.length()
&& predicate.test(text.charAt(start))) {
++start;
}
return start;
}
}
No siempre podemos escapar de los índices, pero nos aseguramos de poner un grado de indirección detrás de una abstracción
Algunas funciones auxiliares
public static int count(String s, char c) {
return count(s, Character.toString(c));
}
public static int count(String s, String sub) {
int total = 0;
int idx = s.indexOf(sub);
while (idx != -1) {
++total;
idx = s.indexOf(sub, idx + 1);
}
return total;
}
private static int discountMarks(int marks) {
return marks == 0
? 0
: marks * 2;
}
private static int discountSpecialCharacters(int chars) {
return chars == 0
? 0
: chars / 2;
}
private int countChars(String s) {
return next.length()
- discountMarks(count(next, "(C)"))
- discountMarks(count(next, "(R)"))
- discountSpecialCharacters(count(next, '^'))
- discountSpecialCharacters(count(next, '~'));
}
Juntando todo
public static String ellipsify(String text, int maxChars) {
if (text.length() <= maxChars) {
return text;
}
WordSplitter splitter = new WordSplitter(text);
StringBuilder builder = new StringBuilder();
int charCount = 0;
while (charCount < maxChars && splitter.hasNext()) {
String next = splitter.next();
if (charCount + next.length() > maxChars) {
break;
}
charCount += countChars(next);
builder.append(next);
}
return builder.append(" ...").toString();
}
¿Lo mejor? Las funciones auxiliares fueron después reutilizadas en otras funcionalidades
Y el código resultante resultó más limpio
Siempre vale la pena tomarse un momento
y pensar en una
forma alterna de desarrollar una historia
10. Parametrizar, parametrizar, parametrizar
Requerimiento #1: En una DB jerárquica necesito buscar el primer nodo que sea de un tipo específico
Requerimiento #2:
Buscar el primer nodo que sea de un tipo específico
que contengan una propiedad específica
Requerimiento #3:
Buscar el primer nodo
que tengan un
nombre específico
¡Requerimiento #n!
Lo único constante
en el desarrollo de
software es el cambio
Una manera simple
de estar preparado al cambio es parametrizar nuestras funciones
¡El API de los lenguajes
lo usan todo el tiempo!
public static void sort(List<T> list, Comparator<T> c)
public Thread(Runnable target)
public void addActionListener(ActionListener l)
Y no es un consejo
nuevo, el patrón de
diseño Component
busca esa ventaja
public static Optional<Resource> getFirstChild(Resource resource,
Predicate<Resource> keep) {
for (Resource child : resource.getChildren()) {
if (keep.test(child)) {
return Optional.of(child);
}
}
return Optional.empty();
}
¡La solución, parametrizada!
Con una sola función abarcamos infinitos
casos de uso, pero
solo necesitaríamos
una prueba unitaria
Utiliza las ventajas de parametrizar los métodos
11. Las pruebas unitarias son documentación
Las pruebas unitarias
son "first-class citizens"
en nuestro código
Todas las buenas prácticas que aplicamos a nuestro código, deben aplicarse a las pruebas unitarias
¡Pero las pruebas unitarias también tienen sus propias buenas prácticas!
Regla cardinal: el código de las pruebas unitarias debe ser trivial de entender
En Construcción
de Software se enseña sobre el uso de las especificaciones
En Java usamos Javadocs
Pero Javadocs como cualquier comentario
es susceptible de desinformación
assertThat(isPalindrome(""), is(true));
assertThat(isPalindrome("a"), is(true));
assertThat(isPalindrome("ab"), is(false));
assertThat(isPalindrome("radar"), is(true));
assertThat(isPalindrome("reader"), is(false));
assertThat(isPalindrome("Radar"), is(true));
assertThat(isPalindrome("A man a plan a canal Panama"), is(true));
assertThat(isPalindrome("A man, a plan, a canal - Panama"), is(true));
Pruebas Unitarias
como especificaciones
Recuerda: el código
debe ser trivial
Implementemos el
tablero del Juego del 15
public final class Tablero {
private final int[][] tablero;
private final int filas;
private final int columnas;
private Coordenada hueco;
public Tablero() {
this.filas = 4;
this.columnas = 4;
this.tablero = new int[filas][columnas];
ordenarTablero();
this.hueco = buscar(0);
desordenar(); // utiliza Random
}
// más código
}
public final class Tablero {
// más código
public Tablero mover(int n) {
if (n < 0 || n >= filas * columnas) {
throw new IllegalArgumentException();
}
Tablero copia = new Tablero(this);
Coordenada número = copia.buscar(n);
copia.mover(número, n);
return copia;
}
}
¿Cómo probamos que podamos mover la ficha debajo del hueco?
Opción 1
- Creamos un tablero
- Llamamos al método toString
- Hacemos "parsing" al String
para encontrar el hueco - Hacemos "parsing" para hallar
el número debajo del hueco - Llamamos a mover()
- Modificamos el String de toString
para producir el tablero resultante
y lo usamos en el assert
¡Yo escribía código así!
Si tenemos ciclos / condicionales, entonces las pruebas unitarias no son triviales de entender
¿Otra opción?
Tablero(int[][] tablero) {
this.tablero = tablero;
this.columnas = tablero.length;
this.filas = tablero[0].length;
this.hueco = buscar(0);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Tablero other = (Tablero) obj;
if (!Arrays.deepEquals(tablero, other.tablero))
return false;
return true;
}
@Test
public void puedoMoverLaFichaAbajoDelHueco() {
Tablero inicial = new Tablero(new int[][] {{1, 2, 3},
{4, 0, 5},
{6, 7, 8}});
Tablero siguiente = new Tablero(new int[][] {{1, 2, 3},
{4, 7, 5},
{6, 0, 8}});
assertThat(inicial.mover(7), equalTo(siguiente));
}
Hacer el código fácil de probar es más importante que otras consideraciones
¡Código como Documentación!
12. KISS
KISS
Keep It Simple
and Straightforward
A la hora de programar, siempre es mejor la solución más simple
La perfección se alcanza, no cuando no hay más que agregar sino cuando no hay nada más que quitar
-- Antoine de Saint-Exupéry
YAGNI
You Aren't Gonna Need It
Resiste la tentación
de agregar cosas que
no se necesitan
Incrementa la complejidad natural del código,
de manera iterativa
Usa TDD o escribe el
código de una manera TDD
13. Vencer el
miedo a decir "no"
En general:
¡Vencer el miedo
a dar una opinión!
En muchísimas ocasiones decir "no", no tiene
ningún impacto negativo en nuestro proyecto
Es mejor decir "no"
desde el principio,
en vez de quedar mal
¡No significa que a todo tengamos que decir "no"!
14. Entender
el negocio
Entender el negocio
de nuestro cliente,
pero también
de nuestra empresa
Este entendimiento le da contexto a las decisiones de ellos y a las nuestras
Tómate el tiempo de conocer a las personas claves de las empresas
15. Vencer el síndrome del
super héroe
¿Qué es más productivo:
no escribir código por
n horas o preguntar?
¡Todos tenemos que preguntar alguna vez!
¡No tiene nada que
ver con la experiencia!
¡A veces solo el hecho
de expresar nuestra duda, es suficiente para
hallar la respuesta!
Preguntar
- Dar un paso atrás
- Buscar compañero de proyecto
- Buscar compañero de trabajo
- Buscar conocido
- Internet
Si no tienen un proceso
de Code Review, pídanlo
Por otro lado,
un proyecto no lo hace
una sola persona:
hay que trabajar en equipo
Team Building
- Comparte tiempo con tu equipo
- Preocúpate por tu equipo
- Tómate el tiempo de solucionar
roces con tu equipo - Tómate el tiempo de
conocer a tu equipo
16. El tiempo
es sagrado
¡No hay un valor más preciado para la mayoría que el tiempo!
Tiempo
- Llega a tiempo a trabajar
- Llega a tiempo a las reuniones
- Organízate para no perder tiempo
En mi experiencia
hay muchas cosas para
las que "no tenía tiempo", que pude hacer
solo organizándome
La gente valora
muchísimo a las personas que son puntuales
17. Ser el peor
de tu equipo
"Siempre sé el peor integrante en cada
banda en la que estás"
-- Pat Metheny
Si tu eres el peor de
un equipo, puedes aprender algo de todos
Cuando ya no tengas nada que aprender de los demás, es probablemente hora
de cambiar de proyecto
Aprovecha la gente con la que trabajas al máximo y trata de aprender de ellos todo lo que puedas
¿Y a ustedes qué
les hubiera
gustado saber?
¡Gracias!
@gaijinco
Lo que me hubiera gustado saber antes de trabajar en la industria del desarrollo de software
By Carlos Obregón
Lo que me hubiera gustado saber antes de trabajar en la industria del desarrollo de software
- 2,347