domingo, enero 30, 2022

Como conseguir trabajo de programador sin titulo Universitario

{esta es una respuesta mía en el Slack de Arg Tech EU - TechEnEuropa.com sobre la pregunta de cómo conseguir trabajo de tech sin titulo universitario}

En el Space a lo que apuntaba es como hacer de cero a uno: como hacer para conseguir el primer trabajo y la primera experiencia laboral sin titulo ni otra experiencia.

Teniendo dos o tres años de experiencia ya es fácil conseguir el numero 4 o 5, pero sino es un Catch 22. Es el meme que dice.. si no tengo experiencia no puedo tener trabajo, y sin trabajo no puedo tener experiencia.

Históricamente, ese loop se rompe con un titulo o con alguien que tenga confianza que vas a poder crecer. Y ahí hay un "hack" que muchos hicimos: empezamos a estudiar y estando en segundo o tercer año nos metemos en una pasantía o trabajo de principiante. 

Hay muchas empresas que contratan estudiantes de segundo-tercer año. Y eso hice yo, empece a trabajar en segundo-tercero, ademas de hacer un montón de cosas por mi cuenta por fuera de la universidad.

Por eso hice la aclaración del titulo, y la respuesta corta es que un titulo es lo mas rápido y eficiente en tiempo y esfuerzo, mucho mas que mirar videos de YouTube, Udemy, o hacer proyectos propios de GitHub.

Y lo digo siendo alguien que me la paso mirando videos de YouTube, comprando cursos de Udemy y con un GitHub lleno de proyectos desde hace mas de 20 años. En mi pagina web personal esta toda mi vida de programación. 

Yo tuve mi titulo mucho después de tener ya experiencia laboral, pero me llevo varios años de hacer muchos proyectos personales y estudiar por mi cuenta. Y también aprendí a programar en el colegio, en mi caso aprendí en el primario desde los 11-12 años.

Se puede perfectamente conseguir un trabajo sin un titulo. Pero todos los consejos tradicionales para buscar trabajo, no te sirve ninguno. No podes ir a una búsqueda laboral de RRHH con un CV de autodidacta y cero experiencia laboral porque no te van a dar ni cinco de atención. 

Así a grandes rasgos, la estrategia es totalmente diferente. 

Tenes que salir por tu cuenta a vender tu tiempo y buscar alguien que quiera trabajar con vos, no como empleado sino como un socio. No digo de hacer tu propia empresa, pero tampoco es muy alejado. 

Mis primeras experiencias de programación fueron justamente así, haciendo programas y proyectos que a mi me interesaban y realmente funcionaban. Los hice para mi propio uso, por mi propia motivación, y sin esperar compensación monetaria a cambio.

  1. hice una pagina web - red social, que se hizo bastante popular
  2. fui uno de los programadores del Argentum Online un juego open source Argentino. Mas detalles en este video.
  3. escribía y participaba de foros y listas de discusión
  4. participaba de eventos y reuniones de programadores
  5. tenia una pagina web personal que yo mismo hice! 

En general, el mensaje es mostrar interés genuino y entusiasmo, y mostrar que uno tiene la capacidad tanto de hacer como de aprender. Programar es entender.

Hacer esto funciona, pero lleva mucho tiempo. Ya dije brevemente en el Space que estoy en otro grupo donde ayudamos a los que recién empiezan a aprender y conseguir trabajo. Ya hay al menos dos personas que sin ningún titulo tienen un trabajo, primero hicieron varios proyectos completos por su cuenta y después tuvieron que salir ellos a buscar alguien que confíe y que los ayude a capacitarse. Al principio no les pagaban, y ellos a cambio estaban todo el día aprendiendo. 

Pero les llevó más de un año hasta que consiguieron dar el primer pie laboral.

Las empresas invierten un montón en sus empleados, muchas veces sin darse cuenta. Ya desde el hecho de tener empleados, es una inversión. Pero en Tech pasa todo el tiempo que sale una nueva versión de X herramienta y la empresa esta obligada a usarla, y sus empleados estan obligados a actualizarse. Eso es una inversión. 

Ya paso con Cloud, Python 3, Node.js, React, Microservicios, la historia se repite cada un par de años. Cuando recien sale React, hubo un antes y un despues... las empresas que hoy hacen React, cuando recien empezaban ya tenian empleados que trabajaban en HTML, CSS, Angular, pero nadie sabia React. 

Los mismos trabajadores, ya con trabajo y sin experiencia, tuvieron que aprender a usar React, Node, etc.

Asi a grandes rasgos, el salto de cero a uno es muy diferente del salto de 3 a 4 o 7 a 8. La primer experiencia laboral es muy, muy diferente a la numero 7.

En ese caso particular,  al contratar a alguien con cero experiencia laboral, la empresa se esta corriendo un riesgo muy grande en contratar a alguien que tal vez no sea lo que ellos necesitan. La empresa no tiene referencia para medir tus skills. 

Además siempre esta la pregunta de por que te van a capacitar para que seis meses después te vayas a otra empresa que paguen mas. Ya está ocurriendo y es lo que salió en el articulo hoy.

domingo, marzo 06, 2016

Compilers construction resources


However, those who wants to be serious about compilers should consider acquiring the books: 
  • Compilers: Principles, Techniques, and Tools. Either 1st or 2nd Edition. I've got the 1st edition from AbeBooks, the basics are still the same today compared to 30 years ago (tokenizer, parser, syntax driven translation, intermediate code generation). 
  • Engineering a Compiler, Second Edition. This one is a good book too, and it talks in more detail about Recursive Descent Parsers, which in practice is what many compilers are doing today (ie, gcc for C++). It is a simple technique, and you can write a RDP by hand easily.

jueves, enero 14, 2016

lunes, septiembre 14, 2015

tl;dr: Python 2 vs Python 3

$ cat main.py
print (u"Hello " + b"World!");

$ python2 main.py
Hello World!

$ python3 main.py
Traceback (most recent call ast):
  File "main.py", line 1, in <module>
    print (u"Hello " + b"World!");
TypeError: Can't convert 'bytes' object to str implicitly

domingo, marzo 29, 2015

Aprendiendo a programar: eligiendo un lenguaje

Si querés aprender a programar y querés elegir un lenguaje, usá uno que te permita aprender los conceptos básicos de programación: Variables, uso de memoria, estructuras de control, flujo de ejecución, expresiones, abstracción, programación modular y reuso de código, recursión, estructuras de datos, algoritmos, eficiencia, programación genérica. Punteros es un tema que todos deberían conocer y estudiar, conocer de su existencia y de su razón.

lunes, enero 19, 2015

12v UPS para Raspbery Pi

Raspberry Pi UPS

Andaba necesitando una forma de mantener mi Raspbery Pi funcionando aún con cortes de luz. Mi circuito está basado en este otro regulador de MRE con la diferencia que lo armé con lo que tenía en mi caja de herramientas. Los materiales son:

  • Batería de gel de 12v 7Ah
  • Cargador de batería de 12v de flote, 1A.
  • Regulador 5V switching (¡una joyita!)
  • Raspberry Pi
  • Relés, Op Amps, resistencias, transistor, etc.
La idea del circuito es muy simple: conmutar entre 12v de un transformador y 12v de una batería de gel en el momento en que el transformador pierde energía. Para esto usamos el circuito de MRE que con un op-amp en modo comparador contra un diodo zener abre o cierra los relés.

La primer modificación importante al circuito de MRE es reemplazar el diodo zener con un LM336Z-5.0 que, a diferencia de un zener, el LM336Z es un integrado para usar como referencia de voltaje, por ejemplo en un instrumento de medifición. 

Este integrado es el mismo recomendado por XQ2FOD en su circuito de regulación de energía solar (muy interesante y vale la pena leerlo), y los que lean con atención verán que tanto el circuito de MRE como de XQ2FOD funcionan con el mismo principio: un op-amp como comparador de tensión.

Mi primer circuito no tenía un comparador de tensión, sino que se conectaban los 12v del transformador directo a los relés. El problema con hacer esto es que los relés necesitan mucha menos corriente que el regulador de 5V, y permanecían cerrados durante un segundo de más, por lo que la Rasperry Pi se reiniciaba. 

El comparador de tensión lo que hace entonces es cortar los relés antes que la tensión caiga por debajo de lo que el regulador de 5V necesita; esto se ajusta manualmente con el preset de 100k R1. Hice la prueba de cortar intermitentemente la tensión de los 12v del transformador y la Raspberry Pi nunca se reinició.

Otra diferencia con el circuito de MRE es que mi circuito usa dos relés en lugar de uno. El primer relé conmuta entre los 12v del transformador de 220v-12v, y el segundo conmuta el cargador de batería hacia la batería cuando hay 220v en la línea. El segundo relé existe para que el cargador no esté conectado a la batería al momento de cortarse los 220v de línea.

El componente importante y final es el regulador switching de 5V. De mi parte usé el RECOM R-78B5.0-1.0 que posee una de conversión eficiencia del 97%, por lo que a diferencia del clásico regulador lineal 7805 no necesita disipador de calor: a 12v 1A el 7805 posee una eficiencia del ~45%.


jueves, enero 08, 2015

oggdec lame pipe: converting ogg to mp3

If this command shows "Warning: unsupported audio format":
oggdec -o - file.ogg | lame - file.mp3
Try using raw mode:
oggdec -R -o - file.ogg | lame -r - file.mp3
Or, if the generated mp3 file is pure noise, swap the bytes with -x:
oggdec -R -o - file.ogg | lame -rx - file.mp3
To convert a batch of multiple ogg files:
for x in *.ogg; do oggdec -R -o - "$x" | lame -rxh - "$x.mp3"; done

miércoles, julio 02, 2014

jueves, junio 26, 2014

Eclipse Luna on Debian Wheezy Crash


The crash is related to a version mismatch between GTK2, GTK3 and Debian's GLIBC. The relevant bug is here: https://bugs.eclipse.org/bugs/show_bug.cgi?id=430736

To force the use of GTK2 on Eclipse Luna you can try:
$ export SWT_GTK3=0
For a more permanent solution you can set the GTK version on the eclipse.ini file:
 --launcher.GTK_version
2
This option should be inserted before the --launcher.appendVmargs option.

And now you should be able to use Eclipse Luna on Wheezy.

domingo, mayo 04, 2014

Substring matching in Python (run between naive, Boyer-Moore, and Suffix Array)

A  few days ago I found this very interesting problem: given a list of strings L, write a function that returns the elements of L which contains some substring S.

For example, given L=["Casa", "Perro", "Gato", "Onomatopeya", "internacionalizacion", "Om nom nom"] and S="nom", we want the result of find(L, S) = ['Onomatopeya', 'Om nom nom'].

Naïve Version

On Python this sounds simple enough, and we can write:
def find1(L, S):
    return [x for x in (L) if S in x]
However, for a big enough L and S we can see the runtime of this function depends not only on the size of L but also on the size of S. That's it, the runtime complexity of find1 in BigOh notation is: O(n.m.s), where:
  • n=len(L)
  • m=max([len(x) for x in L])
  • s=len(S)
The plot of time for find1 for a fixed L and where we increase the size of S looks like this:


Boyer-Moore-Horspool

It turns out that since version 2.5, Python's "in" operator is implemented internally using a modified version of Boyer-Moore algorithm for substring searching. The details are here.

We can take advantage of this detail by creating a temporary structure for faster lookups. We pre-process L so we can make fast queries.

The idea is to construct a big string W with the concatenation of all the elements of L, using a special char as separator, a char that is not present on S nor any element of L. For example:
L = ["Casa", "Perro", "Gato", ...]
W = "Casa\nPerro\nGato\n..."
Then, finding if a substring S is present in any of the elements of L can be answered by just writing: 
S in W
This allows us to answer whether a substring is present or not. To actually construct the resulting list of elements of L which contain S we need another helper structure. We build T, a list of integers that, for every elements in L, equals the starting index of this element in W. Continuing with the example:
T = [0, 5, 11, 16, ... ]
This means the first element, "Casa", starts at index 0 in W; the second element "Perro" starts at index 5 in W, etc. And this structure allows us to quickly determine the index in W for every element in, and we lookup the index by doing a binary search on T.

The runtime complexity for constructing this intermediate index is O(n), with O(n) memory usage.

Our new find function should then:
  • find the first position of S in W as p
  • determine for which element of L this index relates to, by doing a binary search on T
  • from p+1 onwards, find again the next S in W.
Since an element of L can contain many times the same substring S we may jump to the next word on W.

On code, the find2 function looks like:
def find2(L, S):
    # Using the native Boyer-Moore implementation of the "in" operator
    R = []
    i = W.find(S)
    while i != -1:
        p = bisect.bisect_right(T, i) - 1
        e = L[p]
        #assert S in e
        R.append(e)
        i = W.find(S, T[p] + len(e))
    return R
The runtime complexity of this new find function is: O(n.m). We still need to take into account the length of each element of L since BMH algorithm is (mostly) linear on the W string. 

The plot of runtime for find1 vs. find2 looks like the following graphic. Again, we are leaving a fixed L and increasing the size of S:



Suffix Array

There is a third way to solve this problem, by means of constructing a suffix array

This amazing data structure offers a runtime complexity of O(log N) for suffix lookups, where N is the length of the string. Incidentally, it also allows to lookup for substrings, since we just lookup until a suffix on the SA has S as prefix.

Again, we need to construct an intermediate index, which is again very simple: sort all the possible suffixes on W. The trick is how to do it: we shall not keep every possible suffix as an string, but just a list of starting positions for every suffix, and sort this list by the actual string of the suffix.

In code, the construction of the SA table is really simple:
# Suffix Array Table
SL = list(range(len(W)))
SL.sort(key=lambda x: W[x:x+100])
The runtime complexity for constructing this intermediate index is O(n.log n), with O(n) memory usage.

To find a specific suffix we should binary search the SA table, using the element on SL to determine where in W the suffix starts.

Since a substring may appear many times on many elements of S, we may have many sufixes starting with S. The good news is, since the list of suffixes is sorted, all this suffixes will be one after another on the SL table. But since we are doing a binary search on the list of suffixes, we can't be sure on where the middle pointer will jump in this contiguous sequence of suffixes, all starting with S. 

Therefore, when we find the position of some suffix we should go back a little to make sure we are starting on the first suffix on the sequence of suffixes that start with S.

On code, our new find3 function looks like:
def find3(L, S):
    # Suffix array
    start = 0
    end = len(SL)
    while start < end:
        mid = start + (end - start) // 2
        pa = SL_key_fn(W, SL[mid], 100)
        pb = SL_key_fn(S, 0, len(S))
        if pa < pb:
            start = mid + 1
        elif pb < pa:
            end = mid
        else:
            # A word may contain the same S multiple times
            R = set()
            while mid > 0 and W.startswith(S, SL[mid]):
                mid = mid - 1
            if not W.startswith(S, SL[mid]):
                mid = mid + 1
            while mid < len(SL) and W.startswith(S, SL[mid]):
                p = bisect.bisect_right(T, SL[mid]) - 1
                e = L[p]
                assert S in e
                R.add(p)
                mid = mid + 1
            return [(L[i]) for i in R]
    return []
The SL_key_fn function was a failed experiment to enhance the performance of the lookups. This function today is:
def SL_key_fn(data, x, llen):
    return data[x:x+llen]
Which is the same as the key on the SA table sorter.

The runtime performance of the find3 function is: O(log (n.m)), and the plot of the three functions looks like this:



Drawbacks

This SA implementation in Python is using a lot of temporary memory for sorting the table. My implementation on my laptop is using 2.4GB of RAM to sort an L of 150k elements. There's been some discussion about this memory issue on this blog post and in this Stack Overflow question.

Special thanks

Python Argentina community is a great place to look for help for all your spanish Python programming needs. 

Carpintería de fin de semana





martes, abril 22, 2014

module-assistant on Debian with a custom Kernel

If you get this error:

# m-a -v -k"/usr/src/linux-headers-$(uname -r)/" a-i openafs
Updated infos about 1 packages
Bad kernel version specification at /usr/bin/m-a line 568, <$apt> line 9.

You should also use the -l (ell) option:

# m-a -vd -k"/usr/src/linux-headers-$(uname -r)/" -l $(uname -r) a-i openafs


viernes, abril 11, 2014

domingo, marzo 02, 2014

miércoles, febrero 26, 2014

OpenSSL Base64 encoding

I'll just leave this here in case anyone else is interested:


martes, enero 21, 2014

Programs

Programs must be written for people to read, and only incidentally for machines to execute.
Structure and Interpretation of Computer Programs.

sábado, enero 04, 2014

NorCal 49er QRP



Estando de vacaciones y con ganas de hacer un proyecto de radio simple e interesante, me propuse encarar el NorCal 49er QRP. Además, decidí construir mi propia versión del PCB con el Eagle, y publiqué el diseño y demás información en un repositorio de GitHubhttps://github.com/alejolp/49er-qrp.

miércoles, agosto 21, 2013

Ping por Packet AX25

Así me está funcionando el ping por AX25, nada mal!
# ping -i 15 10.0.0.1
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_req=1 ttl=64 time=4002 ms
64 bytes from 10.0.0.1: icmp_req=2 ttl=64 time=2689 ms
64 bytes from 10.0.0.1: icmp_req=4 ttl=64 time=2851 ms
64 bytes from 10.0.0.1: icmp_req=5 ttl=64 time=3382 ms
64 bytes from 10.0.0.1: icmp_req=7 ttl=64 time=3258 ms
64 bytes from 10.0.0.1: icmp_req=9 ttl=64 time=2753 ms
64 bytes from 10.0.0.1: icmp_req=12 ttl=64 time=4070 ms
64 bytes from 10.0.0.1: icmp_req=13 ttl=64 time=3859 ms
64 bytes from 10.0.0.1: icmp_req=15 ttl=64 time=4178 ms
64 bytes from 10.0.0.1: icmp_req=17 ttl=64 time=4264 ms
64 bytes from 10.0.0.1: icmp_req=18 ttl=64 time=3541 ms
64 bytes from 10.0.0.1: icmp_req=20 ttl=64 time=4856 ms
64 bytes from 10.0.0.1: icmp_req=22 ttl=64 time=4018 ms
64 bytes from 10.0.0.1: icmp_req=23 ttl=64 time=5313 ms
64 bytes from 10.0.0.1: icmp_req=25 ttl=64 time=3723 ms
64 bytes from 10.0.0.1: icmp_req=27 ttl=64 time=2809 ms
64 bytes from 10.0.0.1: icmp_req=28 ttl=64 time=4391 ms
64 bytes from 10.0.0.1: icmp_req=29 ttl=64 time=6268 ms
64 bytes from 10.0.0.1: icmp_req=30 ttl=64 time=6058 ms
64 bytes from 10.0.0.1: icmp_req=31 ttl=64 time=4279 ms
64 bytes from 10.0.0.1: icmp_req=32 ttl=64 time=5625 ms
64 bytes from 10.0.0.1: icmp_req=33 ttl=64 time=3763 ms
64 bytes from 10.0.0.1: icmp_req=34 ttl=64 time=2911 ms
64 bytes from 10.0.0.1: icmp_req=35 ttl=64 time=2853 ms
64 bytes from 10.0.0.1: icmp_req=36 ttl=64 time=2718 ms
64 bytes from 10.0.0.1: icmp_req=37 ttl=64 time=2789 ms
64 bytes from 10.0.0.1: icmp_req=38 ttl=64 time=2944 ms
64 bytes from 10.0.0.1: icmp_req=39 ttl=64 time=3699 ms
64 bytes from 10.0.0.1: icmp_req=41 ttl=64 time=3798 ms
64 bytes from 10.0.0.1: icmp_req=44 ttl=64 time=2918 ms
^C
--- 10.0.0.1 ping statistics ---
44 packets transmitted, 30 received, 31% packet loss, time 645337ms
rtt min/avg/max/mdev = 2689.509/3819.856/6268.794/986.186 ms

viernes, agosto 16, 2013

Medidor de ROE digital con Arduino

Arduino SWR Meter

Tal como adelanté en mi anterior entrada, una mejora al puente de ROE de XQ2FOD que le hice fue la de reemplazar los medidores analógicos por un display LCD controlado con un Arduino (ATmega328p).

Arduino SWR Meter

El circuito es demasiado simple como para dibujarlo, la idea es usar la librería LiquidCristal de Arduino y reemplazar en el circuito de XQ2FOD la entrada a los medidores analógicos por las entradas Analog0 y Analog1 en el Arduino. Se pueden ver más fotos en mi galería de Flickr.

El código fuente del programa cargado en el Arduino se puede encontrar acá. En programa se encarga de leer constantemente las entradas 0 y 1 analógicas, y hacer los cálculos de SWR (sacados del capítulo de Lineas de Transmisión del ARRL Handbook 2010).

p = sqrt(Pr/Pf);
SWR = (1+p) / (1-p);

Además como se puede ver en el codigo fuente, se calcula el promedio de los datos leídos para evitar que el display LCD parpadee cuando los datos de entrada fluctúen en el tiempo.

LU4EXT