Learning a second language has been my best career decision.
Literally, it changed my life and doubled my salary.
Initially, I learned English to make my CV look more attractive to recruiters. But learning English has been more than a better CV for me.
It took me a couple of years to learn English and an extra couple of years to take full advantage of it. I went to a traditional language school: lots of grammar exercises and “repeat after me” sessions.
With the right method and consistency, you could learn a language faster. One year is a good time frame to reach a conversational level. Of course, your mileage may vary.
If you’re learning a second language to boost your career, prepare for your next vacation abroad, or connect with your extended family, follow these 7 proven steps:
1. Fluency Isn’t Perfection
Before learning a single word or grammar rule, understand that nobody speaks any language perfectly.
I don’t speak perfect English. I don’t even speak perfectly in my native language. Nobody speaks perfectly in any language.
Speaking a language isn’t about making zero mistakes. It’s about communication and human connection.
Fluency isn’t perfection.
With that myth debunked, let’s move to the next step.
2. Identify Your Goals
Start your language journey by identifying your goal and your time frame.
A clear goal helps you determine what skills you need to focus on and what vocabulary to master. Studying to pass a language certification exam in a few months is different from learning useful phrases for your next vacation.
To keep that goal in mind, set a language checkpoint or milestone. For example, your first checkpoint could be a 15-minute conversation with a native speaker after your first month of practice.
3. Find a Learning Method
The key to learning a new language is to tie it to activities you enjoy doing.
I enjoy listening to podcasts and watching videos. When I started to learn French, I found a podcast for beginners and followed it during my lunch breaks. While everybody else at the office was taking a nap, I was with my headphones on, listening and taking notes.
From Scott Young’s Ultralearning book, the guy behind the “One Year Without English” challenge, I learned to spend about 10% of the learning time finding resources and study guides.
Spend the first days, looking up good resources to learn your target language.
4. Find a Teacher or a Language Partner
No matter how shy you think you are, sooner or later you will have to open your mouth and let words out.
My best strategy is to find a teacher or language partner for one-to-one sessions. Personally, I have used iTalki to find my teachers and language partners. Some teachers offer structured lesson plans or only conversation practice. But with language partners, expect only chit-chat about anything. Usually, you split the practice session in half: switching languages each time.
I have to confess I’ve been lucky with my language partners. After several practicing sessions with a language partner, we became friends and met in real life. It happened we were on vacation in the same city. And, thanks to those unexpected life turns, my language partner’s husband was a lawyer and ended up helping me review my contracts before joining my first job at an international software agency.
A teacher or language partner will help you practice in a stress-free environment.
If you’re learning a language for work, ask them to role-play being your interviewer or a participant at a meeting you have to conduct.
5. Practice a Little Bit Every Day
“Little by little, a little becomes a lot.” — Jim Kwik, the Brain Coach.
If you want to see fast results, study a bit every day. Learn 5 phrases or listen to a short audio a day. That’s better than a long study session once a week.
If you think you don’t have time, take advantage of every dead time slot in your schedule: while waiting in lines, on the bus, or walking home.
Back in the day when I had to commute, I reviewed my Anki flashcards and listened to podcasts on my way home.
If you’re not used to practicing a language daily, tie your study or practice sessions to an already-built habit. Do you have to do the dishes? The laundry? Listen to an audio lesson while doing it. You said you don’t have already built habits? What about listening to podcasts while in the shower? You shower regularly, right?
6. Learn Phrases Instead of Words
Don’t memorize words or grammar rules.
I know it’s frustrating when your teacher asks you to open a textbook to work on grammar rules in your first class. It kills all the fun of learning a language.
Instead of learning individual words, learn complete phrases and use them as LEGO bricks you assemble and interchange. You will learn words in context along with their gender. Yes, words have gender in other languages. And it’s funny when one word has a different gender in your target language.
Also, don’t overthink grammar rules or find logic in your target language.
When we were children, we didn’t receive a grammar book to learn our native languages. We absorbed grammar rules after being exposed to the language for hours and hours.
7. Immerse Yourself in the Language
This is the #1 tip to study from home – Create a language environment around you and your hobbies:
Change your phone and computer language
Read the news or watch TV
Follow social media
Listen to music
You name it!
You don’t have to travel abroad to learn a language, you can do it in the comfort of your bed.
Parting Thoughts
Thanks to learning a new language, I’ve made good friends, doubled my salary, and discovered a passion for learning and teaching.
We become a new person with every language we learn. I feel like I’m more extroverted when speaking English.
I have found that there’s no better way to build trust with coworkers or clients at work than to say hi, thanks, yes/no, and bye in their target language. It will bring smiles to their faces and show you’re interested in their culture.
So if you’re looking for a twist in your career, try a new language. Try Arabic to work in the oil industry, German for the automotive industry, or Spanish to join a rising startup in Latin America…who knows!
A second language could change your life too.
Refactor your coding career with my free 7-day email course. In just 7 emails, you’ll get 10+ years of career lessons to avoid costly career mistakes.
For months, I ran a series called Monday Links, here on my blog. Every time I found interesting posts about programming and software engineering, I compiled them until I had five or six and then published them in a post.
It was a way to document what I read and react to the other people’s posts, when my reaction didn’t merit a separate post. Think of Twitter/X threads on my blog.
I’m moving my Monday Links to an email list.
The format is almost the same. Every Friday, I send four or five posts about software engineering and programming. But I sprinkle other interesting subjects in between. Also, I include one of my own posts published the same week and share updates about the projects I’m creating.
I hope I don’t have to write a CV anymore. I hope you don’t either.
A personal brand is the new CV and portfolio.
But since you’re reading this one, probably you still need to write a CV. I wish it won’t end up in the dumpsters behind a company careers page. I’ve tried that route with zero results.
More than once, some friend or ex-coworker has asked me to take a look at his CV. I’ve asked them too.
I realized I had already given (and received) the same tips about CVs. These are five tips I always give to write better CVs as software engineers.
1. Keep Your CV to Only One Page
These days of endless scrolling on TikTok, nobody has time and attention to read more than that.
Make it easy for people to review your CV. More than two pages are too long.
Imagine your CV is a business card in a A4 page. You only have one or two minutes to introduce yourself in a business card. Do the same in a CV.
Keep font size, spaces, and borders consistent.
2. Don’t Include a Photo, Marital Status, National ID, or Other Personal Information
Only add your professional email and LinkedIn profile, or any other social media profile to showcase your expertise. GitHub, Medium, dev.to handles work too.
I even skip phone numbers. You don’t want strangers calling or texting you in the middle of the night for a “life changing opportunity at a well-know company in the tech sector.”
You will be asked for personal information once you’re hired, not before.
And only include your city and country if you’re applying for a position that requires being in a particular location. Don’t put your home address either.
3. Don’t Add Every Single Experience Since You Started Working
Use your most recent experience. The last five years, for example.
And, include only the experience relevant to the job you’re applying for. Don’t add the unrelated part-time job you had in your first year at university.
You see? Even the one in the picture has only one page. Photo by João Ferrão on Unsplash
4. Don’t Include a Cloud of Keywords, Languages, or Library Names
Stars or years working with a language don’t mean expertise.
Probably, you’re riding a bike since you’re 4 or 5, but it doesn’t give you a place in the Tour de France. It’s the same with years of experience with a language or tool. What does five years with XML mean?
Instead, describe what you did and what accomplishments you made in your previous jobs. Use numbers in your job descriptions.
For example, “I wrote an order processing component that scaled up to X orders per month by…“ instead of “C#/ASP.NET Core/SQL Server.” Somebody reading your CV would wonder what you did with those tools.
A bunch of names by themselves mean nothing.
5. Skip Fancy Lines Like “Honest, Hard-Working, Team Player”
Everybody uses the same buzzwords.
“Team player,” “detail-oriented,” and “enthusiast” only take extra space in your single-page CV. Do you still remember tip 1, right? And guess what? Everybody desperate for a job is a detail-oriented enthusiast team player who like to work under pressure in their CV.
Instead of fancy lines, describe in a paragraph what you do and how you can help your next employer.
Voilà! Five tips as promised. Remember recruiters, technical managers, and CTOs will read your CV looking for different information, not only a cloud of languages and libraries.
A CV is to start an interview process. Keep it short and to the point. Be clear. “If you confuse, you lose.”
Repositories are the least SOLID part of our codebases.
When we work with Domain-Driven Design, we take care of our business domain and forget about our data-access layer. We end up dumping, in a single interface, every combination of methods and parameters to retrieve our entities from the database. This way, we break the Single Responsibility Principle and Interface Segregation Principle. You see? They’re the least SOLID part.
Our repositories become so bloated that to use one specific method from a repository, we end up depending on a huge interface with lots of other single-use methods. GetOrdersById, GetOrdersByDate, GetLineItemsByOrderId…
The Specification Pattern Simplifies Repositories
With the Specification pattern, we extract the “query logic” to another object and away from our repositories.
Instead of making our repositories more specific by adding more methods (_repo.GetOrderById(123456)), the Specification pattern makes repositories more general (_repo.FirstOrDefault(new OrderById(123456))).
Think of a Specification as the query logic and the query parameters to retrieve objects.
Specifications make more sense when using Domain-Driven Design. With Specifications, we encapsulate the LINQ queries, scattered all over our code, inside well-named objects that we keep inside our Domain layer.
usingArdalis.Specification;// <--usingArdalis.Specification.EntityFrameworkCore;// <--usingMicrosoft.EntityFrameworkCore;varbuilder=WebApplication.CreateBuilder(args);builder.Services.AddScoped(typeof(Repository<>));// <--builder.Services.AddDbContext<MoviesContext>(options=>options.UseInMemoryDatabase("MovieDb"));varapp=builder.Build();app.MapGet("/movies/{releaseYear}",async(intreleaseYear,Repository<Movie>repo)=>{varbyReleaseYear=newMoviesByReleaseYear(releaseYear);// <--varmovies=awaitrepo.ListAsync(byReleaseYear);// <--// As an alternative, with Ardalis.Specification we can// use a specification directly with a DbContext://var movies = await aDbContext.Movies// .WithSpecification(byReleaseYear)// .ToListAsync();returnResults.Ok(movies);});app.MapPost("/movies",async(Moviemovie,Repository<Movie>repo)=>{awaitrepo.AddAsync(movie);awaitrepo.SaveChangesAsync();// Or, simply with a DbContext://await aDbContext.Movies.AddAsync(movie);//await anyDbContext.SaveChangesAsync();returnResults.Created($"/movies/{movie.Id}",movie);});app.Run();publicclassMoviesByReleaseYear:Specification<Movie>// ^^^^^{publicMoviesByReleaseYear(intreleaseYear){Query.Where(m=>m.ReleaseYear==releaseYear).OrderBy(m=>m.Name);}}publicrecordMovie(intId,stringName,intReleaseYear);publicclassRepository<T>:RepositoryBase<T>whereT:class// ^^^^^{publicRepository(MoviesContextdbContext):base(dbContext){}}publicclassMoviesContext:DbContext{publicMoviesContext(DbContextOptions<MoviesContext>options):base(options){}publicDbSet<Movie>Movies{get;set;}}
Ardalis.Specification provides a RepositoryBase<T> class that wraps our DbContext object and exposes the database operations using Specification objects. The ListAsync() receives a specification, not an IQueryable object, for example.
Our Repository<T> is simply a class definition without query logic. Just a couple of lines of code.
Now the query logic is inside our MoviesByReleaseYear. Ardalis.Specification translates those filtering and ordering conditions to the right chain of Entity Framework Core methods.
Our repositories are way simpler and the query logic is abstracted to another object.
A Naive Implementation of the Specification Pattern
The best way to understand a piece of code is to recreate a minimal version.
Here’s a naive and bare-bones implementation of an in-memory repository that filters a list using a specification object:
varmovies=new[]{newMovie("Terminator 2",1991),newMovie("Totoro",1988),newMovie("Saving Private Ryan",1998)};varrepo=newInMemoryRepo<Movie>(movies);// <--varfound=repo.List(newByReleaseYearSpec(1998));// <--// Output:// [ Movie { Name = Saving Private Ryan, ReleaseYear = 1998 } ]publicrecordMovie(stringName,intReleaseYear);publicclassByReleaseYearSpec:Spec<Movie>// <--{publicByReleaseYearSpec(intreleaseYear){Where(movie=>movie.ReleaseYear==releaseYear);}}publicabstractclassSpec<T>// <--{publicFunc<T,bool>?Predicate{get;set;}publicvoidWhere(Func<T,bool>predicate){Predicate=predicate;}}publicclassInMemoryRepo<T>{privatereadonlyIEnumerable<T>_items;publicInMemoryRepo(IEnumerable<T>items){_items=items;}publicIEnumerable<T>List(Spec<T>spec)// ^^^^ {varevaluator=newSpecEvaluator();returnevaluator.ApplySpec(_items,spec).ToList();}}publicclassSpecEvaluator{publicIEnumerable<T>ApplySpec<T>(IEnumerable<T>items,Spec<T>spec){// The original implementation uses some "evaluators" to:// 1. Check if the spec has a certain shape and // 2. Apply that shape to the input "query"//// For example, WhereEvaluator checks if the spec has a Where clause and// applies it.// // This SpecEvaluator would be a Composite of smaller// evaluators that look for a certain shapeif(spec.Predicate!=null){returnitems.Where(spec.Predicate);}returnitems;}}
All the magic is inside the InMemoryRepo and the SpecEvaluator classes.
In the original implementation, the SpecEvaluator takes the parameters from the filters inside our specification (like Where, OrderBy, Skip, and Take) and apply them using EntityFramework Core methods into the IQueryable object that represents our database query.
Voilà! That’s how to use the Specification pattern to make our repositories more SOLID. With the Specification pattern, our repositories have a slim interface and a single responsibility: to turn specifications into database calls.
.NET 6.0 replaced the Startup class with a new hosting model and a simplified Program.cs file.
The Startup class is still available in newer versions. If we’re migrating a pre-.NET 6.0 project, the .NET upgrade assistant tool does the work while keeping the Startup class.
Here are 3 alternatives to handle with the Startup class when migrating to newer versions:
1. Official Docs Approach
Newer versions of ASP.NET Core work perfectly fine with the old Program.cs file and Startup class, we can choose to do nothing and keep them.
If we want to keep the Startup class, here’s what Microsoft official docs show to make the Startup class work with the new hosting model and the simplified Program.cs:
Turn the methods from the Startup class into private methods in the new Program.cs file.
varbuilder=WebApplication.CreateBuilder(args);ConfigureServices(builder.Services);// ^^^^^varapp=builder.Build();Configure(app,app.Environment);// ^^^^^^app.Run();// This method used to be in Startup.csstaticvoidConfigureServices(IServiceCollectionservices){services.AddControllers();}// This method used to be in Startup.cs toostaticvoidConfigure(IApplicationBuilderapp,IWebHostEnvironmentenv){app.UseRouting();app.UseAuthorization();app.UseEndpoints(endpoints=>{endpoints.MapControllers();});}
3. Do-It-Yourself Approach
And if we want our Program.cs to look like the newer ones, there’s no automatic tool for that (at least I couldn’t find one in my Googling session). We have to copy the contents of the Startup class into the Program.cs file, by hand:
varbuilder=WebApplication.CreateBuilder(args);// Put here what you had in ConfigureServices...builder.Services.AddControllers();varapp=builder.Build();// Put here what you had in Configure...app.UseAuthorization();// Before://// app.UseRouting();// ASP0014: Suggest using top level route registrations instead of UseEndpoints//app.UseEndpoints(endpoints =>//{// endpoints.MapControllers();//});//// After:app.MapControllers();app.Run();
We cut and pasted the content of Startup inside the right sections of the new Program.cs. This time, we get some warnings about deprecated methods and their alternatives.
Voilà! If you’re a lazy developer like me, do nothing or go with the approach from the official docs. Otherwise, go with any of the other two options.