La mejor forma de aprender un nuevo lenguaje.

Actualmente tengo 2 cursos gratuitos de lenguajes de programación puros, llamados Java para Principiantes y Kotlin para Principiantes, y pienso lanzar algunos otros próximamente, creo que Python será el siguiente.

La forma en que organizo y explico mis cursos parte de la idea que tengo de que la mejor manera de aprender un lenguaje de programación es esta:

1.- Aprende lo básico rápidamente, no te metas a ver cosas complejas o te aburrirás antes de usar el lenguaje en algo práctico.

2.- Usa el lenguaje con un framework (Java -> Android, Python -> Django, Javascript -> NodeJS, etc) para crear algo divertido, una web o app. Sigue el mismo principio aprendiendo solo lo necesario.

3.- Aprende cosas más complejas del lenguaje conforme las vayas necesitando.

4.- Ahora sí poco a poco profundiza más en los detalles y temas avanzados y vuélvete experto(a).

Debido a que en este tipo de cursos he recibido comentarios como “No profundiza lo suficiente” o “Faltan temas por ver” aquí te menciono que vas y que no vas a encontrar en mis cursos donde enseño lenguajes de programación puros para Principiantes ANTES de que avances más, para que sepas de antemano si el curso es para ti y no te lleves una decepción si no es lo que esperas.

Qué encontrarás en estos cursos:

  • La sintaxis y partes principales del lenguaje: Cómo implementar variables, métodos, clases, interfaces, etc.
  • Principales estructuras de datos para el lenguaje.
  • Consejos y prácticas útiles de programación.

Qué NO encontrarás en los cursos:

  • Temas teóricos avanzados como patrones de diseño. Este tipo de temas los iré agregando en el canal de Youtube y este blog.
  • Implementación del lenguaje en un framework: para implementación de Java en Android está el curso de Android completo con Java.
  • Todo lo que no sea absolutamente necesario para empezar a usar el lenguaje cuanto antes.

Los cursos “para Principiantes” son para ti si eres nuevo(a) en el lenguaje y quieres aprender rápidamente lo más importante del lenguaje para empezar a usarlo cuanto antes sin gastar mucho tiempo y abrumarte. Directo y al grano.

Ahora lo contrario, estos cursos NO son para ti si ya sabes usar bien el lenguaje o si lo que quieres es dominar todo hasta un nivel experto y con un enorme detalle.

En resumen, la idea es que aprendas lo más rápido posible, por lo que trato de que los cursos sean 100% prácticos y te enseñen justo lo necesario para especializarte en lo que quieras, evitando que tengas que tomar un curso de 30 horas solo para empezar a usar el lenguaje.

Tal como menciono arriba, en este blog y el canal de Youtube Hackaprende iré añadiendo temas complementarios y/o más avanzados. Todas las ideas y recomendaciones son más que bien recibidas, por ejemplo si crees que debería agregar algún tema que piensas que es esencial y actualmente no incluyo. También si crees que algún tema actual no está claro te agradecería mucho que me lo comunicaras. Por ahora te felicito por iniciar uno de los cursos y ¡Te deseo mucho éxito!

Android: executePendingBindings() en RecyclerViews

Como nada es perfecto, data binding tiene una desventaja con respecto a findViewById().

Android “pinta” los views en la pantalla cada 16ms en promedio, esta actualización de la pantalla puede suceder en menos tiempo, pero si se hacen procesos pesados en el hilo principal (Main Thread) podría causar que la actualización sea de más de 16ms y empezamos a ver lags en la pantalla. Cuando usamos binding para asignar valores a un view, por ejemplo al asignar un texto a un TextView con binding.myTextView.text = “Some text”, el binding no se ejecuta al instante, sino que espera a que pase ese tiempo de 16ms para medir cuanto el área del nuevo texto y luego pintarlo. Es como cuando juegas con tu perro a lanzar la pelota y va por ella pero luego no la suelta hasta que pasas un rato tratando de quitársela (16ms en promedio).

Esto por lo general no nos afecta, por ejemplo cuando abrimos una nueva Activity y pintamos todo con binding, 16ms no son nada. Lanzaste la pelota a tu perro una vez y se la quitas, no pasa nada.

Pero ahora imagina un recyclerView con decenas de elementos, cuando haces scroll rápidamente y viene el siguiente elemento, puede ser que todavía no hayan pasado los 16ms recomendados, pero podría suceder que cuando aparece el siguiente elemento el binding no haya alcanzado a medir y pintar el anterior, entonces podrían verse lags extraños. Es como si quisieras romper un record de lanzar pelotas y ya viene la siguiente pero tu perro todavía no suelta la anterior.

Esto es poco común, y de hecho si no implementas lo que estoy a punto de mencionarte muchas veces no tendrás problemas, pero es mejor prevenir.

La solución para prevenir esto es que después de pintar los views de tu ViewHolder, mandes llamar binding.executePendingBindings(), así:

inner class EqViewHolder(private val binding: ItemBinding): RecyclerView.ViewHolder(binding.root) {
fun bind(earthquake: Earthquake) {
binding.myTextView.text = someString
binding.myImageView.src = someImage
// Más cosas que hagas con tus views
...
// JUSTO AQUÍ VA 👇
binding.executePendingBindings()
}
}

De esta manera, vas a forzar al binding a que no se espere los 16ms, sino que pinte todo inmediatamente. Sencillo, es solo agregar una linea de código, aunque Android no explica mucho para qué sirve en su documentación oficial y por eso a veces es difícil de recordar.

Espero que los de Android manejen esto automáticamente en el futuro. Mientras tanto te invito a dejar tus comentarios.

Curso Android Completo con Kotlin ¡Ya disponible!

Finalmente después de tanto planear, grabar, editar, corregir y revisar, el nuevo curso de Android Completo con Kotlin ya está listo.

Este curso incluye todos los temas necesarios e importantes para empezar a trabajar como desarrollador Android, usando las mejores prácticas recomendadas por Google. Traté de hacer el curso lo más ameno posible, incluyendo solo los temas importantes, como siempre yendo directo al grano y dejando fuera todo lo que no sea esencial para que puedas aprender en el menor tiempo posible, como rayo ⚡️.

Sin más te invito a que le des un vistazo, aquí encontrarás más información sobre el curso, lo que incluye y lo que necesitas para empezar.

https://hackaprende.com/pagina-de-inicio/cursos/android-completo-con-kotlin/

¡Para los suscriptores del blog hay un súper descuento del 60%!, envíame un correo a hackaprende@gmail.com y si eres suscriptor te mandaré el super cupón.

¡Saludos y mucho éxito!

Comentarios claros en Kotlin.

Recientemente me di a la tarea de documentar el código de una aplicación que estoy desarrollando y seamos sinceros, documentar código es aburrido, tal vez por esto no pude encontrar muy buena información, en particular en la parte de cómo comentarlo. Incluso en la página oficial de Kotlin no vienen ejemplo claros de cómo comentar el código. Después de un buen rato de prueba y error descubrí algunas cosas que creo que vale la pena compartir.

Vamos a verlo con el ejemplo de un método que obtenga el subtotal de una lista del supermercado donde cada elemento tiene un precio, una cantidad y un posible descuento. Además al final le vamos a agregar un impuesto 😭 al ticket. El método podría ser el siguiente.

private fun getTicketTotal(productList: MutableList<Product>, 
taxPercentage: Double): Double {
    var total = 0.0
    for (product in productList) {
        val subtotal = (product.quantity * product.price)
        total += subtotal * ((100 - discount)/100)
    }

    val taxAmount = (total * taxPercentage)/100
    total -= taxAmount

    return total
}

Empecemos por lo más básico, un simple comentario en Kotlin se inserta con dos lineas diagonales // así:

private fun getTicketTotal(productList: MutableList<Product>, 
taxPercentage: Double): Double {
    // Creamos total en 0
    var total = 0.0
    for (product in productList) {
        // Subtotal es el la multiplicación de cantidad por precio
        val subtotal = (product.quantity * product.price)
        total += subtotal * ((100 - discount)/100)
    }

    // Sumamos el impuesto al total
    val taxAmount = (total * taxPercentage)/100
    total -= taxAmount

    return total
}

Para comentarios más largos que ocupen varios renglones podemos insertarlos con
/* ... */, aunque también podríamos tener simplemente varios renglones con //:

private fun getTicketTotal(productList: MutableList<Product>, 
taxPercentage: Double): Double {
    // Creamos total en 0
    var total = 0.0
    for (product in productList) {
        // Subtotal es el la multiplicación de cantidad por precio
        val subtotal = (product.quantity * product.price)
        /* Restamos el posible descuento al subtotal, por ejemplo si 
           el subtotal es de $150 el descuento es de 30%, el total 
           será de 150 * ((100 - 30)/100) = 150 * 0.7 = 105 */
        total += subtotal * ((100 - discount)/100)
    }

    // Obtenemos el monto de impuestos, por ejemplo si el total es 
    // 150 y el impuesto es 15%: 
    // taxAmount = (150 * 15)/100 = 22.5
    val taxAmount = (total * taxPercentage)/100
    total -= taxAmount

    return total
}

Finalmente, para comentar un método completo podemos utilizar /** ... */, aquí podemos indicar también cuales son los parámetros que necesita el método y qué es lo que regresa, así:

/**
* Este método obtiene el total de una lista de [Productos] teniendo
* en cuenta [Producto.precio], [Producto.cantidad], 
* [Producto.descuento] y un posible impuesto.
*
* @param productList La lista de productos de la cual se obtiene el * total
* @param taxPercentage El porcentaje, el cual puede ir de 0 a 100
*
* @return El total después de impuestos.
**/
private fun getTicketTotal(productList: MutableList<Product>, 
taxPercentage: Double): Double {
    // Creamos total en 0
    var total = 0.0
    for (product in productList) {
        // Subtotal es el la multiplicación de cantidad por precio
        val subtotal = (product.quantity * product.price)
        /* Restamos el posible descuento al subtotal, por ejemplo si 
           el subtotal es de $150 el descuento es de 30%, el total 
           será de 150 * ((100 - 30)/100) = 150 * 0.7 = 105 */
        total += subtotal * ((100 - discount)/100)
    }

    // Obtenemos el monto de impuestos, por ejemplo si el total es 
    // 150 y el impuesto es 15%: 
    // taxAmount = (150 * 15)/100 = 22.5
    val taxAmount = (total * taxPercentage)/100
    total -= taxAmount

    return total
}

Como puedes observar hay palabras que encerré entre [] esto es para que puedas dar option + clic en esa clase o incluso en un método o campo dentro de la clase y te lleve directamente a ella. Ahora es mucho más claro lo que hace el método, sí es aburrido, pero tus compañeros (y tu yo del futuro) te lo agradecerán.

NOTA: No todos los métodos e instrucciones necesitan comentarios, hay unos que son auto explicativos.

Si te gustó el artículo compártelo suscríbete y sígueme en mis redes sociales o donde quieras. También te invito a dejar tus comentarios. ¡Saludos y mucho éxito!

Tensorflow en 90 segundos

Recientemente he retomado mi interés por el machine learning y su combinación con las aplicaciones móviles, estaré posteando acá lo que vaya aprendiendo e implementando, para iniciar quiero compartir este pequeño video acerca de qué se trata una de las tecnologías más usadas hoy en día para Machine Learning, se llama Tensorflow y te lo presento en tan solo 90 segundos.

Te invito a suscribirte al canal y al blog para más cursos, artículos, tutoriales y todo tipo de contenido relacionado al diseño, programación y emprendimiento tecnológico.

Usando Corrutinas con Firebase en Android MVVM

Estoy desarrollando un proyecto alterno que utiliza la arquitectura MVVM y Firebase/Firestore, para todo el manejo de los hilos y tareas en segundo plano estoy usando corrutinas (coroutines).

Me acabo de topar con una situación que no me había sucedido, al usar Firebase, este trae sus propios listeners para saber cuando termina un proceso, el cual regresa un DocumentSnapshot que es el que necesitas para extraer los datos. Algo así:

class MyRepository(private val firestoreDb: FirestoreDb) {
    
    suspend fun downloadSomething(listener: (DocumentSnapshot) -> Unit){
        firestoreDb.collection(collectionName).get()
            .addOnCompleteListener {
                task ->
            if (task.isSuccessful) {
               // Do something ...
               listener(myDocumentSnapshot)
            } else {
               // Show error
            }
        }
    }
}

Esto es llamado desde el ViewModel el cual se vería así.

viewModelScope.launch {
    repository.downloadSomething {
        documentSnapshot ->
        // Do Something
    }
}

Pero uno de los puntos principales de las corrutinas es que no necesitemos integrar interfaces y/o listeners para devolver los datos, sino que los podamos devolver en la misma linea para continuar con nuestro código, es decir, en vez de esto:

fun downloadSomething() {
repository.downloadSomething {
result ->
processResult(result)
}
}

Con las corrutinas podemos hacer esto:

suspend fun downloadSomething() {
val result = repository.downloadSomething()
processResult(result)
}

Pero tal y como funciona normalmente Firebase no se puede porque como dije antes, ya trae ese listener .addOnCompleteListener integrado, entonces ¿Cómo lo manejamos para poder usar el resultado con corrutinas? La respuesta que encontré fue usar .await()

await() nos ayuda a esperar a que un proceso se complete sin bloquear otros hilos (Como el Main Thread), haciendo que evitemos tener que llamar un listener una vez que se complete, ya que al terminar se ejecutará la siguiente linea de código del hilo. Pero await() no viene con Firebase sino con las Kotlin Coroutines, por lo que tenemos que agregarlo como dependencia:

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.1.1'

Ahora que ya podemos usar await() el código de Firebase quedaría así:

class MyRepository(private val firestoreDb: FirestoreDb) {

    suspend fun downloadSomething(): DocumentSnapshot {
        return withContext(Dispatchers.IO) {
            firestoreDb.collection(collectionName).get().await()
        }
    }
}

Con esto podemos regresar el DocumentSnapshot y continuar con nuestro trabajo de forma secuencial en el ViewModel:

viewModelScope.launch {
    val documentSnapshot = repository.downloadSomething()
    // Continuar secuencialmente
}

Esto trae ventajas para hacer testing, para manejar excepciones, para cumplir con el MVVM y para manejar los LiveData.

NOTA: Esto solo funciona para cuando usas get().addOnCompleteListener(), todavía no he encontrado la manera de hacerlo funcionar con cambios en tiempo real con addSnapshotListener(), buscaré la manera para postear.

Como siempre todos los comentarios y dudas son más que bienvenidos.

¿Qué es JSON? – En palabras simples

Algunos de mis alumnos me han pedido que les pase un artículo sobre JSON y como funciona, es más sencillo de lo que crees, aquí te dejo un video para que conozcas JSON en 3 minutos.

Como siempre los comentarios o dudas son bienvenidos. Inscríbete al canal y al blog para recibir los últimos artículos, videos, cursos, etc que voy creando.

¡A programar! ¿Qué lenguaje/framework/IDE elegir para lanzar tu idea?

Después de todo lo anterior: Validar tu idea, identificar a tus clientes, planear tu MVP, lo que sigue es empezar a “tirar código”. Pero este paso puede llevarte al análisis parálisis, habiendo tantas tecnologías ¿Qué debería usar? ¿Qué lenguaje? ¿Qué IDE? ¿Qué framework?. La realidad es que aquí si depende del proyecto (No te puedo decir que uses Android si tu idea es una página web) pero si te puedo dar algunos consejos que me han sido útiles en este punto.

La regla general es que si ya conoces una de las tecnologías y esa te sirve para construir tu idea no pierdas el tiempo leyendo esto y vete por ese lenguaje, framework, etc, ya después aprenderás otras cosas. Si no conoces nada sobre programación, trataré de recomendarte la tecnología que además de servirte para construir tu idea te deje habilidades que te servirán para el futuro.

  • Si tu idea es una app móvil, piensa si está enfocado en Android, iOS o ambos:
    • Solo Android: Usa Android Studio con cualquiera de los dos lenguajes: Java o Kotlin. Aquí tienes un artículo para elegir cuál de los dos. Y acá puedes encontrar dos cursos gratuitos, uno para cada lenguaje.
    • Solo iOS: No hay pierde, usa Swift con XCode.
    • Ambas: Programar apps en iOS y Android a la vez te puede tomar mucho tiempo. Si no tienes un socio o amigo que te pueda ayudar a programar en iOS mientras tu lo haces en Android o viceversa lo mejor es usar Flutter, es un lenguaje creado por Google que te ayudará a desarrollar apps en ambas plataformas a la vez y viene con mucho poder últimamente, una pequeña desventaja es que no puedes hacer tantas cosas como en los lenguajes nativos, así que es para apps más simples.
  • Si tu idea es una página web:
    • Blog, tienda en linea, landing para tu empresa: Si tu página es algo sencillo como estas mencionadas no necesitas ni siquiera aprender a programar. Elige un servicio de los que te permiten hacer tu página web en un par de horas: WordPress, Wix, Shopify, WooCommerce, Squarespace. Todas estas te ahorrarán mucho tiempo, y el tiempo te dirá si necesitas desarrollar una página más compleja.
    • Una página más compleja que necesita una API: Si ya sabes programar en un lenguaje busca si existe un framework con ese lenguaje que te permita crear tu backend, por ejemplo yo conozco Python, me gusta el lenguaje, así hago que mis páginas web en Django, un framework para Python que me ayuda a hacer las cosas rápidamente porque puedo programar tanto frontend (Lo que el usuario ve) como el backend (La lógica detrás), si no sabes programar te recomiendo este camino. Otros ejemplos son NodeJs para JavaScript, Spring para Java, Spring para Kotlin, Rails para Ruby.

Lo sé, suena a mucho trabajo, y lo es, pero tómalo como algo divertido que te servirá toda tu vida. Si quieres dedicarte a ser programador como empleado sí necesitas aprender lo más que puedas para conseguir el empleo, pero como esta sección es para emprendimiento, mi consejo es que no te pases la vida estudiando cada tecnología hasta sus huesos, aprende solo lo necesario para implementarlo en tu idea y más adelante te vas adentrando conforme lo necesites.

¿Hay algún lenguaje o framework que recomiendes o que hayas escuchado buenas cosas de él? Te invito a dejarlo en los comentarios.

¿Cómo vas a ganar dinero con tu idea? Los mejores 8 modelos de negocio para tu app.

El modelo de negocio es la manera en que vas a ganar dinero con tu idea, la forma en que vas a cobrar; por ejemplo podrías vender productos, manejar subscripciones, etc. En este artículo ahorrarás buen tiempo al conocer los 8 modelos de negocio más comunes para aplicaciones móviles, páginas web u otros productos tecnológicos. ¡Vamos a ello!

  1. Enterprise: Vender servicios o software a otra empresa. Normalmente estos contratos tienen términos fijos y vienen con renovación al final de un periodo. Ejemplos: Moz, Docker, Cloudera, Solidworks.

  2. SaaS (Software as a Service): Vender licencias de software en la nube, por lo general a otras empresas. Normalmente el pago es mensual o anual y no hay plazos fijos, mientras pagues tienes acceso. Ejemplos: Github, Mixpanel, Slack.

  3. Subscripción: Vender un producto o servicio, usualmente a clientes directos, en una base recurrente. Ejemplos: Netflix, Spotify, Shave Club.

  4. Transaccional: Hacer transacciones financieras a nombre de un cliente y cobrar una tarifa, usualmente un porcentaje de la transacción. Ejemplos: Stripe, Paypal, Conekta.

  5. Marketplace: Ser intermediario en la venta de un producto o servicio, es decir, estás en medio de los vendedores y compradores, y cobrar una tarifa o porcentaje del valor de la transacción. Ejemplos: Airbnb, Mercado Libre, Uber, Rappi, Udemy.

  6. E-Commerce: Vender productos físicos en línea. Generalmente las empresas e-commerce manufacturan y llevan inventarios de estos productos. Ejemplos: Boutiques en linea como Shein o Mango, venta directa de productos de cualquier tipo a través de una página web.

  7. Publicidad: Ofrecer un servicio gratuito a los consumidores y ganar dinero con publicidad (Recuerda que si el producto es gratis, significa que tú eres el producto 😉). Ejemplos: Redes sociales, blogs, apps gratuitas con publicidad.

  8. Hardware: Vender dispositivos físicos a consumidores o negocios. Ejemplos: Go Pro, Xiaomi.

Para terminar te dejo dos consejos para elegir el mejor modelo de negocios para tu producto de acuerdo a errores que cometí con mis proyectos pasados.

  • Tu producto debería caer en uno de estos 8 modelos, en este tema es en el único que te diría que no intentes reinventar el hilo negro. Entender la forma en que van a pagar es algo muy importante para los usuarios, si el modo de pago es confuso puede causar rechazo por no saber cuánto van a pagar exactamente o incluso puede sonar a estafa.

  • El modelo de negocios es parte de la idea, ve pensando en él desde el inicio de tu proyecto para que lo tomes en cuenta en el diseño.

¿Tu producto está dentro de uno de estos modelos? ¿Conoces algún otro modelo de negocios probado? Te invito a dejar tus comentarios.

Data Binding para Android con Kotlin

Desde el inicio de Android la forma de obtener acceso a un View era así:

val nameTextView: TextView = findViewById(R.id.name_text) as TextView

Y estaba bien, solo que se podía mejorar porque tiene sus desventajas:

  1. Es aburrido de hacer cuando tienes 10 o más views, tienes que ir a ver a tu layout.xml el id que le diste. Además ¿Qué es eso de tener que escribir 2 veces el tipo de View que vas a usar? 😪.

  2. Si olvidaste crear el nameTextView y lo quieres usar la app truena (nullPointerException), pero no te avisa en tiempo de compilación, sino en tiempo de ejecución, es decir, te avisa ya que estás usando la app, cuando no hay mucho que hacer más que lanzar una nueva versión, esto es tardado y para entonces ya les tronó a muchos de tus usuarios.

  3. Cada vez que llamas findViewById, Android va y busca el id de entre el montón que tienes en tu app, es un proceso pesado y poco eficiente.

Por eso Google decidió sacar a la luz algo llamado data binding (Algo así como enlace de datos) que hace tu código más rápido, limpio y menos repetitivo.

Lo primero que hay que hacer es agregar soporte para data binding en tu archivo build.gradle (app):

// En la parte superior agrega el plugin de 'kotlin-kapt'
plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-kapt' <--- Aquí
}

android {
    ...

    buildFeatures {
        dataBinding true
    }
}

Luego en tu layout.xml (por ejemplo activity_main.xml), y en la pestaña del documento o en el ViewGroup padre da clic en alt + N (option + N en Mac) y selecciona “Convert to data binding layout”

Verás que todo se encierra dentro de <layout

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            id="@+id/name_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    </LinearLayout
</layout>

Esto también creará automáticamente una clase binding con el nombre de tu Activity, por ejemplo para MainActivity creará una clase ActivityMainBinding, o para LoginActivity creará ActivityLoginBinding

Ahora en el método onCreate() de tu Activity, en vez de usar setContentView(R.layout.activity_main) puedes crear un objeto binding así:

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

Y en vez de usar el findViewById(R.id.name_text) simplemente podrás usar el binding.nameText y esto será tu TextView, por ejemplo para agregarle texto puedes hacer lo siguiente

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    val binding = ActivityMainBinding.inflate(layoutInflater)
    setContentView(binding.root)

    // binding.nameText es el TextView de tu layout, esto es lo mismo que hacer
    // findViewById(R.id.name_text)
    val nameTextView = binding.nameText
    nameTextView.text = "Ragnar Lothbrok"

    // También puedes simplemente escribir 
    // binding.nameText.text = "Ragnar Lothbrok"
}

El nameText del binding.nameText se generó automáticamente a partir del id del TextView name_text, por ejemplo si tienes un ImageView llamado profile_image, se generaría también un binding.profileImage que podrías usar directamente, no más findViewById.

Con dataBinding obtienes 3 ventajas que se contraponen a las desventajas del inicio:

  1. El código es más limpio y ahorras lineas de código porque no necesitas declarar los views.

  2. Los errores ocurren en tiempo de compilación, si ocurre un error lo sabrás antes de ejecutar la app.

  3. Como todo se pre-carga en tiempo de compilación, el proceso es más eficiente al usar los views.