<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ray Mathew</title>
    <description>The latest articles on DEV Community by Ray Mathew (@raymathewdeveloper).</description>
    <link>https://dev.to/raymathewdeveloper</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F483866%2F9794e20f-6d96-43a5-95e8-d2b6fc6bc643.jpg</url>
      <title>DEV Community: Ray Mathew</title>
      <link>https://dev.to/raymathewdeveloper</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/raymathewdeveloper"/>
    <language>en</language>
    <item>
      <title>2024: Career Review</title>
      <dc:creator>Ray Mathew</dc:creator>
      <pubDate>Mon, 30 Dec 2024 00:32:32 +0000</pubDate>
      <link>https://dev.to/raymathewdeveloper/2024-career-review-1p4</link>
      <guid>https://dev.to/raymathewdeveloper/2024-career-review-1p4</guid>
      <description>&lt;p&gt;This year is different. I haven’t been employed for 10 months. But I’ve not been idle either.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Milestones and moments:&lt;/strong&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;I left Thoughtworks&lt;/strong&gt;&lt;br&gt;
I had not wanted it to end though. I liked the culture, where developers were treated as the most important employees, and up-skilling was a high priority. I liked being in the presence of so many smart people. It made me a better person just by listening to them talk to each other. &lt;/p&gt;

&lt;p&gt;But life rarely works the way you plan it. I moved to the US to be with my wife, and I had to resign. My biggest regret - not being able to work with more than one client in my entire 4 years there.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;I do not have a mentor for the first time in my career.&lt;/strong&gt; &lt;br&gt;
A side effect of being unemployed. I had had always had one, even when I had become the Team Lead. Now, while I’m waiting for a new beginning in a new company, I don’t really have anyone. I guess it doesn’t matter that much, since I’m sure to find one again. But I think this thought matters to me because I felt sad when I had to say goodbye to my last mentor, who had left TW just before I did.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;AWS Certification&lt;/strong&gt;&lt;br&gt;
Now that I had left the bubble of Thoughtworks and the tough client (who will continue to not be named), I needed to work on things that were in high demand in the industry. I decided to go for an AWS Developer Associate certification, since that was the buzzword I had heard most while at Thoughtworks, and also many people seemed to be getting jobs with that.&lt;/p&gt;

&lt;p&gt;I spent 4-5 months studying for it. It was a lot to absorb. Even after 5 practice papers and maybe a few hundred questions, I couldn’t get more than 85%. There was so much to learn, and a lot of it could be acquired through real-world experience only.&lt;/p&gt;

&lt;p&gt;But, &lt;a href="https://www.credly.com/badges/c0cba24f-79ee-425f-b4bf-c4a9c34baac7/public_url" rel="noopener noreferrer"&gt;I got it&lt;/a&gt;. And just in time - I also got my work permit approved in the same week.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The US dev job market is a nightmare.&lt;/strong&gt;&lt;br&gt;
I didn’t believe it when I read on Reddit how people applied to 500+ companies without getting an offer. Surely it can’t be that bad? I had cracked my very first application and interview every time I looked to switch in my career back in India!&lt;/p&gt;

&lt;p&gt;But it &lt;em&gt;was&lt;/em&gt; bad. I stopped keeping a track of the companies I had applied to - I was so fed up. Some companies responded with a rejection 4 months later, while others instantly rejected my resume within minutes. &lt;/p&gt;

&lt;p&gt;I have applied to 300+ companies so far, out of which I got just 7 interviews. Referrals didn’t help either. Microsoft, Netflix, Airbnb, Amazon - all rejected my resume. I was sure that I would at least get an interview and dazzle them with my System Design skills.&lt;/p&gt;

&lt;p&gt;My confidence wavered. I was second-guessing myself. For example,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I decided to redo my resume in the boring format with Latex, in case it was getting auto-rejected by some tool. &lt;/li&gt;
&lt;li&gt;I created multiple resumes, for Frontend, Fullstack and TL roles. &lt;/li&gt;
&lt;li&gt;I was told by multiple people to emphasize my technical skills more, and my TL responsibilities less. What the hell! It was the opposite in India.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Out of the companies I did get interviews with - IBM, Nike, Amazon (on the 7th try) and Clipboard Health - I was rejected in the first round. And here I met my next roadblock. One I had been avoiding for 9 years.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;DSA&lt;/strong&gt;&lt;br&gt;
I had tried to study it multiple times over the years, but always found it too difficult. Over time I decided that it was no longer important, because a good company would value the TL skills I have, and the huge tech stack I’ve worked with.&lt;/p&gt;

&lt;p&gt;Nope. First off, there are very few TL roles available. And of the few, many jumped straight to Principal Engineer. And almost all the junior and senior levels roles required DSA knowledge. I didn’t even bother applying to the ones that said DSA was mandatory.&lt;/p&gt;

&lt;p&gt;After 5 months, I decided I could no longer avoid it. I decided to take a paid course, practice some Leetcode, and get to applying to a lot more openings.&lt;/p&gt;

&lt;p&gt;And that’s where I am right now. I’m doing a 5 course series on Algorithms over 4 months on Coursera. Let’s see where it gets me. On the plus side, I at least understand everything now, apart from the insane theorems in the CLRS textbook. I've already passed the first course with 99% grade.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;I’ve decided to make a niche for myself.&lt;/strong&gt;&lt;br&gt;
This came through multiple motivations. First, I had a lot of free time now, and I didn’t want to spend all of it studying for interviews. Second, I still don't have much of an online reputation as a developer. Third, there are a lot of things to learn in the developer space. Fourth, I had been writing ideas for years on things I could build, but never found the time. &lt;/p&gt;

&lt;p&gt;And so, the niche is this: Build a product that I or someone else would actually use. Make it like a real product that can be shipped. And build it using a language, framework or tool that’s famous but I've never used before. When I'm done, write about how I built it, and repeat the process with a new product. &lt;/p&gt;

&lt;p&gt;In this way, I learn a ton of stuff, I finally get to work on my ideas, and I get to be known as ‘that guy who always builds stuff’. Maybe at some point I can even learn stuff like mechanical design, and combine it with coding.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;What has come out of the niche so far.&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;An API written in Golang, to simulate Materia Fusion from Crisis Core.&lt;/strong&gt; &lt;br&gt;
I am super proud of this. In the big picture, this is a very basic GET api with a ton of if-else conditions. But it’s my first ever project that took several weeks to complete. And it’s in a shippable state, ready to be used by others. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/RayMathew/crisis-core-materia-fusion-api" rel="noopener noreferrer"&gt;Github link&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://towardsdev.com/building-an-api-with-go-postgresql-google-cloud-and-cockroachdb-78d78938c5db" rel="noopener noreferrer"&gt;Building an API with Go, PostgreSQL, Google Cloud and CockroachDB&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@raymathew/everything-i-learnt-while-building-an-api-tool-with-go-postgresql-and-google-cloud-for-the-game-3268581e1ccb" rel="noopener noreferrer"&gt;Everything I learnt while building an API tool&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the process, I strengthened my Go knowledge and learnt how to use a Cloud service (Google), how to use PostgreSQL (a big mental block), and a Cloud DB service (CockroachDB).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;A NextJS fullstack website, to showcase hikes and travels done by my wife and I.&lt;/strong&gt;&lt;br&gt;
Another project I’m very proud of. Because my wife and I will actually use this. She is the primary consumer of this app. &lt;/p&gt;

&lt;p&gt;As of this writing, the desktop version is ready, and the mobile version is in the works. I don’t think I’ve done a great job with the UX. And I’m not sure how much I need to learn to get good at it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/RayMathew/travel-history-viewer" rel="noopener noreferrer"&gt;Github link&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Through this I learnt NextJS (and about Vercel, the geniuses who made React fullstack) and, very importantly, Typescript. And I got to use Notion API as well, where all the data is stored.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;/ol&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;Notable firsts:&lt;/strong&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Experienced the nightmare job market in US.&lt;/li&gt;
&lt;li&gt;Learnt and used Cloud services.&lt;/li&gt;
&lt;li&gt;Got a certification that actually matters in the industry.&lt;/li&gt;
&lt;li&gt;Contributed significantly to open-source. &lt;a href="https://github.com/Buzzpy/Dev-Encyclopedia" rel="noopener noreferrer"&gt;https://github.com/Buzzpy/Dev-Encyclopedia&lt;/a&gt; (At the time of writing, I’m credited as the top contributor by the owner).&lt;/li&gt;
&lt;/ol&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;New things learnt:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;AWS. I’ve only scratched the surface though.&lt;/li&gt;
&lt;li&gt;Google Cloud.&lt;/li&gt;
&lt;li&gt;CockroachDB.&lt;/li&gt;
&lt;li&gt;Basics of PostgreSQL.&lt;/li&gt;
&lt;li&gt;Typescript. Again, just the surface.&lt;/li&gt;
&lt;li&gt;NextJS.&lt;/li&gt;
&lt;li&gt;AstroJS.&lt;/li&gt;
&lt;li&gt;Open Graph meta tags.&lt;/li&gt;
&lt;li&gt;Entity tags.&lt;/li&gt;
&lt;li&gt;A shit ton of DSA stuff.&lt;/li&gt;
&lt;/ol&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;Reflections:&lt;/strong&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Knowing what I know now after 6 months of job hunting, I would’ve studied DSA first before the AWS certification. &lt;/li&gt;
&lt;li&gt;Taking this long a break from my career has made me truly appreciate that a job is a just a job, and money can be obtained through other sources too.&lt;/li&gt;
&lt;li&gt;It’s hard to stay motivated for months when you have no concrete goals or deadlines. It requires constant discipline to keep building towards something.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;My plan for 2025: Get a job. Work on side projects. Write. It’s about just getting my life in order. No grand ambitions for the first time.&lt;/p&gt;

</description>
      <category>yearinreview</category>
      <category>devresolutions2024</category>
      <category>2024</category>
    </item>
    <item>
      <title>Building an API with Go, PostgreSQL, Google Cloud and CockroachDB</title>
      <dc:creator>Ray Mathew</dc:creator>
      <pubDate>Wed, 23 Oct 2024 19:27:29 +0000</pubDate>
      <link>https://dev.to/raymathewdeveloper/building-an-api-with-go-postgresql-google-cloud-and-cockroachdb-f1a</link>
      <guid>https://dev.to/raymathewdeveloper/building-an-api-with-go-postgresql-google-cloud-and-cockroachdb-f1a</guid>
      <description>&lt;p&gt;I built an API with Go and PostgreSQL, set up a CI/CD pipeline with Google Cloud Run, Cloud Build, Secret Manager and Artifact Registry, and connected the Cloud Run instance to CockroachDB.&lt;/p&gt;

&lt;p&gt;The API is based on the game &lt;a href="https://en.wikipedia.org/wiki/Crisis_Core:_Final_Fantasy_VII" rel="noopener noreferrer"&gt;Crisis Core: Final Fantasy VII&lt;/a&gt;, to simulate “Materia Fusion”. This article’s intended audience is for developers who just want to know how to build and deploy the API. I have another article where I talk about everything I learnt while working on this project, what didn’t work, and understanding and translating the game’s materia fusion rules (link coming soon).&lt;/p&gt;

&lt;h3&gt;
  
  
  Links for easy reference
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/RayMathew/crisis-core-materia-fusion-api" rel="noopener noreferrer"&gt;GitHub repo and README&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://crisis-core-materia-fusion-api-546461677134.us-central1.run.app/docs/index.html" rel="noopener noreferrer"&gt;Swagger (OpenAPI) documentation and testing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.postman.com/raymathew/crisis-core-materia-fusion-api/overview" rel="noopener noreferrer"&gt;Public Postman collection&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gamefaqs.gamespot.com/psp/925138-crisis-core-final-fantasy-vii/faqs/75088/materia-combination-list" rel="noopener noreferrer"&gt;Source of domain model&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  API Objective
&lt;/h3&gt;

&lt;p&gt;3 endpoints — health check (GET), list of all materia (GET), and simulate materia fusion (POST)&lt;/p&gt;

&lt;h2&gt;
  
  
  The Domain Model
&lt;/h2&gt;

&lt;p&gt;Materia (both singular and plural) is a crystal orb that serves as a source of magic. There are 144 distinct materia in the game, and they’re broadly classified into 4 categories: “Magic”, “Command”, “Support” and “Independent”. However, for the purpose of figuring out the rules of materia fusion, it was easier to have &lt;strong&gt;32 internal categories&lt;/strong&gt; based on their fusion behaviour, and &lt;strong&gt;8 grades&lt;/strong&gt; within those categories (&lt;a href="https://gamefaqs.gamespot.com/psp/925138-crisis-core-final-fantasy-vii/faqs/75088/materia-combination-list" rel="noopener noreferrer"&gt;see reference&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;A materia becomes ‘Mastered’ when it is used for a certain duration. The duration is not important here.&lt;/p&gt;

&lt;p&gt;Most importantly, 2 materia can be fused to produce a new materia. The rules governing fusion are influenced by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Whether either or both materia are mastered.&lt;/li&gt;
&lt;li&gt;Which materia comes first (as in &lt;code&gt;X+Y&lt;/code&gt; is not necessarily equal to &lt;code&gt;Y+X&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Materia internal category.&lt;/li&gt;
&lt;li&gt;Materia grade.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe5anut9hkmn14o2d3z6l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe5anut9hkmn14o2d3z6l.png" alt="Materia Fusion" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And there are a LOT of exceptions, with some rules having 3 levels of nested &lt;code&gt;if-else&lt;/code&gt; logic. This eliminates the possibility of creating a simple table in the DB and persisting 1000+ rules into it, or coming up with One Formula To Rule Them All.&lt;/p&gt;

&lt;p&gt;In short, we need:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A table &lt;code&gt;materia&lt;/code&gt; with columns &lt;code&gt;name(string)&lt;/code&gt;, &lt;code&gt;materia_type(ENUM)&lt;/code&gt; (the 32 internal categories), &lt;code&gt;grade(integer)&lt;/code&gt;, &lt;code&gt;display_materia_type(ENUM)&lt;/code&gt; (the 4 categories used in the game), &lt;code&gt;description(string)&lt;/code&gt; and &lt;code&gt;id(integer)&lt;/code&gt; as an auto-incrementing primary key.&lt;/li&gt;
&lt;li&gt;A data structure to encapsulate the basic rules format &lt;code&gt;MateriaTypeA + MateriaTypeB = MateriaTypeC&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Code to use the basic and complex rules to determine the output Materia in terms of its internal category and grade.&lt;/li&gt;
&lt;/ol&gt;

&lt;h1&gt;
  
  
  1. Setting Up The Local PostgreSQL DB
&lt;/h1&gt;

&lt;p&gt;Ideally you can install the DB from the &lt;a href="https://www.postgresql.org/download/" rel="noopener noreferrer"&gt;website&lt;/a&gt; itself. But the pgAdmin tool could not connect to the DB for some reason, so I used Homebrew.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;brew install postgresql@17
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will install a whole bunch of CLI binary files to help use the DB. Optional: add &lt;code&gt;/opt/homebrew/opt/postgresql@17/bin&lt;/code&gt; to &lt;code&gt;$PATH&lt;/code&gt; variable. Next, create the DB:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# create the DB
createdb materiafusiondb
# step into the DB to perform SQL commands
psql materiafusiondb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create the user and permissions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- create an SQL user to be used by the Go server
CREATE USER go_client WITH PASSWORD 'xxxxxxxx';

-- The Go server doesn't ever need to add data to the DB. 
-- So let's give it just read permission.
CREATE ROLE readonly_role;
GRANT USAGE ON SCHEMA public TO readonly_role;

-- This command gives SELECT access to all future created tables. 
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO readonly_role;

-- If you want to be more strict and give access only to tables that already exist, use this:
-- GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_role;

GRANT readonly_role TO go_client;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create the table
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TYPE display_materia_type AS ENUM ('Magic', 'Command', 'Support', 'Independent');

CREATE TYPE materia_type AS ENUM ('Fire', 'Ice', 'Lightning', 'Restore', 'Full Cure', 'Status Defense', 'Defense', 'Absorb Magic', 'Status Magic', 'Fire &amp;amp; Status', 'Ice &amp;amp; Status', 'Lightning &amp;amp; Status', 'Gravity', 'Ultimate', 'Quick Attack', 'Quick Attack &amp;amp; Status', 'Blade Arts', 'Blade Arts &amp;amp; Status', 'Fire Blade', 'Ice Blade', 'Lightning Blade', 'Absorb Blade', 'Item', 'Punch', 'SP Turbo', 'HP Up', 'AP Up', 'ATK Up', 'VIT Up', 'MAG Up', 'SPR Up', 'Dash', 'Dualcast', 'DMW', 'Libra', 'MP Up', 'Anything');

CREATE TABLE materia (
    id integer NOT NULL,
    name character varying(50) NOT NULL,
    materia_type materia_type NOT NULL,
    grade integer NOT NULL,
    display_materia_type display_materia_type,
    description text
    CONSTRAINT materia_pkey PRIMARY KEY (id)
);

-- The primary key 'id' should auto-increment by 1 for every row entry.
CREATE SEQUENCE materia_id_seq
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;

ALTER SEQUENCE materia_id_seq OWNED BY materia.id;

ALTER TABLE ONLY materia ALTER COLUMN id SET DEFAULT nextval('materia_id_seq'::REGCLASS);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add the data
&lt;/h3&gt;

&lt;p&gt;Create an Excel sheet with table header and data, and export it as a CSV file. Then run the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COPY materia(name,materia_type,grade,display_materia_type,description) FROM
 '&amp;lt;path_to_csv_file&amp;gt;/materiadata.csv' DELIMITER ',' CSV HEADER;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  2. Creating the Go Server
&lt;/h1&gt;

&lt;p&gt;Create the boilerplate code using &lt;a href="https://autostrada.dev/" rel="noopener noreferrer"&gt;autostrada.dev&lt;/a&gt;. Add the options of &lt;code&gt;api&lt;/code&gt;, &lt;code&gt;postgresql&lt;/code&gt;, &lt;code&gt;httprouter&lt;/code&gt; , &lt;code&gt;env var config&lt;/code&gt;, &lt;code&gt;tinted logging&lt;/code&gt;, &lt;code&gt;git&lt;/code&gt;, &lt;code&gt;live reload&lt;/code&gt;, &lt;code&gt;makefile&lt;/code&gt;. We end up getting a file structure like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;📦 codebase
├─ cmd
│  └─ api
│     ├─ errors.go
│     ├─ handlers.go
│     ├─ helpers.go
│     ├─ main.go
│     ├─ middleware.go
│     └─ server.go
├─ internal
│  ├─ database --- db.go
│  ├─ env --- env.go
│  ├─ request --- json.go
│  ├─ response --- json.go
│  └─ validator
│     ├─ helpers.go
│     └─ validators.go
├─ go.mod
├─ LICENSE
├─ Makefile
├─ README.md
└─ README.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  .env file
&lt;/h3&gt;

&lt;p&gt;The boilerplate generator has created code to fetch environment variables and add them to the code, but we can make it easier to track and update the values.&lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;&amp;lt;rootfolder&amp;gt;/.env&lt;/code&gt; file. Add the following values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;HTTP_PORT=4444
DB_DSN=go_client:&amp;lt;password&amp;gt;@localhost:5432/materiafusiondb?sslmode=disable
API_TIMEOUT_SECONDS=5
API_CALLS_ALLOWED_PER_SECOND=1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the godotenv library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go get github.com/joho/godotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following to &lt;code&gt;main.go&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// At the beginning of main():
err := godotenv.Load(".env") // Loads environment variables from .env file
if err != nil { // This will be true in prod, but that's fine.
  fmt.Println("Error loading .env file")
}


// Modify config struct:
type config struct {
  baseURL string
  db      struct {
    dsn string
  }
  httpPort                 int
  apiTimeout               int
  apiCallsAllowedPerSecond float64
}

// Modify run() to use the new values from .env:
cfg.httpPort = env.GetInt("HTTP_PORT")
cfg.db.dsn = env.GetString("DB_DSN")
cfg.apiTimeout = env.GetInt("API_TIMEOUT_SECONDS")
cfg.apiCallsAllowedPerSecond = float64(env.GetInt("API_CALLS_ALLOWED_PER_SECOND"))

// cfg.baseURL = env.GetString("BASE_URL") - not required
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Middleware and Routes
&lt;/h3&gt;

&lt;p&gt;The boilerplate already has a middleware to recover from panics. We will add 3 more: Content-Type checking, rate-limiting and API timeout protection.&lt;/p&gt;

&lt;p&gt;Add &lt;code&gt;tollbooth&lt;/code&gt; library for rate-limting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go get github.com/didip/tollbooth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update &lt;code&gt;&amp;lt;rootfolder/api/middleware.go&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (app *application) contentTypeCheck(next http.Handler) http.Handler {
 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  if r.Header.Get("Content-Type") != "application/json" {
   app.unsupportedMediaType(w, r)

   return
  }
  next.ServeHTTP(w, r)
 })
}


func (app *application) rateLimiter(next http.Handler) http.Handler {
 limiter := tollbooth.NewLimiter(app.config.apiCallsAllowedPerSecond, nil)
 limiter.SetIPLookups([]string{"X-Real-IP", "X-Forwarded-For", "RemoteAddr"})

 return tollbooth.LimitHandler(limiter, next)
}


func (app *application) apiTimeout(next http.Handler) http.Handler {
 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  timeoutDuration := time.Duration(app.config.apiTimeout) * time.Second

  ctx, cancel := context.WithTimeout(r.Context(), timeoutDuration)
  defer cancel()

  r = r.WithContext(ctx)

  done := make(chan struct{})

  go func() {
   next.ServeHTTP(w, r)
   close(done)
  }()

  select {
  case &amp;lt;-done:
   return
  case &amp;lt;-ctx.Done():
   app.gatewayTimeout(w, r)
   return
  }
 })
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The middleware need to be added to the routes. They can be either added to all the routes, or to specific ones. In our case, Content-Type checking (that is, mandating the input headers to include &lt;code&gt;Content-Type: application/json&lt;/code&gt;) is only needed for POST requests. So modify &lt;code&gt;routes.go&lt;/code&gt; as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (app *application) routes() http.Handler {
 mux := httprouter.New()

 mux.NotFound = http.HandlerFunc(app.notFound)
 mux.MethodNotAllowed = http.HandlerFunc(app.methodNotAllowed)

 // Serve the Swagger UI. Uncomment this line later
 // mux.Handler("GET", "/docs/*any", httpSwagger.WrapHandler)

 mux.HandlerFunc("GET", "/status", app.status)
 mux.HandlerFunc("GET", "/materia", app.getAllMateria)

 // Adding content-type check middleware to only the POST method
 mux.Handler("POST", "/fusion", app.contentTypeCheck(http.HandlerFunc(app.fuseMateria)))

 return app.chainMiddlewares(mux)
}

func (app *application) chainMiddlewares(next http.Handler) http.Handler {
 middlewares := []func(http.Handler) http.Handler{
  app.recoverPanic,
  app.apiTimeout,
  app.rateLimiter,
 }

 for _, middleware := range middlewares {
  next = middleware(next)
 }

 return next
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Error handling
&lt;/h3&gt;

&lt;p&gt;Add the following methods to &lt;code&gt;&amp;lt;rootfolder&amp;gt;/api/errors.go&lt;/code&gt; to help the middleware functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (app *application) unsupportedMediaType(w http.ResponseWriter, r *http.Request) {
 message := fmt.Sprintf("The %s Content-Type is not supported", r.Header.Get("Content-Type"))
 app.errorMessage(w, r, http.StatusUnsupportedMediaType, message, nil)
}

func (app *application) gatewayTimeout(w http.ResponseWriter, r *http.Request) {
 message := "Request timed out"
 app.errorMessage(w, r, http.StatusGatewayTimeout, message, nil)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Request and Response structure files
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;rootfolder&amp;gt;/api/dtos.go&lt;/code&gt; for the responses of all 3 endpoints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

// MateriaDTO provides Materia details - Name, Description and Type (Magic / Command / Support / Independent)
type MateriaDTO struct {
 Name        string `json:"name" example:"Thunder"`
 Type        string `json:"type" example:"Magic"`
 Description string `json:"description" example:"Shoots lightning forward dealing thunder damage."`
}

// StatusDTO provides status of the server
type StatusDTO struct {
 Status string `json:"Status" example:"OK"`
}

// ErrorResponseDTO provides Error message
type ErrorResponseDTO struct {
 Error string `json:"Error" example:"The server encountered a problem and could not process your request"`
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;&amp;lt;rootfolder&amp;gt;/api/requests.go&lt;/code&gt; for the POST &lt;code&gt;\fusion&lt;/code&gt; endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package main

import (
 "github.com/RayMathew/crisis-core-materia-fusion-api/internal/validator"
)

// MateriaFusionRequest provides input Materia names and their Mastered states
type MateriaFusionRequest struct {
 Materia1Mastered *bool               `json:"materia1mastered" example:"true"`
 Materia2Mastered *bool               `json:"materia2mastered" example:"false"`
 Materia1Name     string              `json:"materia1name" example:"Fire"`
 Materia2Name     string              `json:"materia2name" example:"Blizzard"`
 Validator        validator.Validator `json:"-"`
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Validator&lt;/code&gt;, from the generated code, will be used later to validate the input fields for &lt;code&gt;\fusion&lt;/code&gt; endpoint.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Structure for the combination rules
&lt;/h3&gt;

&lt;p&gt;Create the file &lt;code&gt;&amp;lt;rootfolder&amp;gt;/internal/crisis-core-materia-fusion/constants.go&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package crisiscoremateriafusion

type MateriaType string

const (
 Fire              MateriaType = "Fire"
 Ice               MateriaType = "Ice"
 Lightning         MateriaType = "Lightning"
 Restore           MateriaType = "Restore"
 ... 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;full list of 32 MateriaTypes can be found &lt;a href="https://github.com/RayMathew/crisis-core-materia-fusion-api/blob/main/internal/crisis-core-materia-fusion/constants.go" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Create the file &lt;code&gt;&amp;lt;rootfolder&amp;gt;/internal/crisis-core-materia-fusion/models.go&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package crisiscoremateriafusion

type Materia struct {
 Name        string `json:"name"`
 Type        string `json:"materia_type"`
 DisplayType string `json:"display_type"`
 Description string `json:"description"`
 Grade       int    `json:"grade"`
}

// Data structure to hold all basic combination rules
type BasicCombinationRule struct {
 FirstMateriaType     MateriaType
 SecondMateriaType    MateriaType
 ResultantMateriaType MateriaType
}

var FILBasicRules = []BasicCombinationRule{
 {Fire, Fire, Fire},
 {Ice, Ice, Ice},
 {Lightning, Lightning, Lightning},
 {Fire, Ice, Lightning},
 {Ice, Fire, Lightning},
 {Fire, Lightning, Ice},
 {Lightning, Fire, Ice},
 {Lightning, Ice, Fire},
 {Ice, Lightning, Fire},
}

var ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;full list of rules can be found &lt;a href="https://github.com/RayMathew/crisis-core-materia-fusion-api/blob/main/internal/crisis-core-materia-fusion/models.go" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handler for GET &lt;code&gt;\materia&lt;/code&gt; in &lt;code&gt;api/handlers.go&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (app *application) getAllMateria(w http.ResponseWriter, r *http.Request) {
 var allDisplayMateria []MateriaDTO
 var allMateria []ccmf.Materia
 var err error

 allMateria, err = app.getAllMateriaFromApprSource()

 if err != nil {
  app.serverError(w, r, err)
 }

// Some materia have the same name but different grades. 
// We need to allow only unique names are sent in the response.
 seenMateriaNames := make(map[string]bool)
 for _, materia := range allMateria {
  if _, isDuplicate := seenMateriaNames[materia.Name]; !isDuplicate {
   seenMateriaNames[materia.Name] = true
   allDisplayMateria = append(allDisplayMateria, MateriaDTO{
    Name:        materia.Name,
    Type:        materia.DisplayType,
    Description: materia.Description,
   })
  }
 }

 err = response.JSON(w, http.StatusOK, allDisplayMateria)
 if err != nil {
  app.serverError(w, r, err)
 }
}

func (app *application) getAllMateriaFromApprSource() (allMateria []ccmf.Materia, err error) {
 // Check if allMateria data is in cache
 if data, found := app.getCachedData(string(ccmf.AllMateriaCacheKey)); found {
  // Type assertion: assert that data is of type []Materia
  if allMateriaCache, ok := data.([]ccmf.Materia); ok {
   allMateria = allMateriaCache
   app.logger.Debug("cache hit")
  } else {
   app.logger.Error("Failed to assert cached data as []Materia")
   return nil, errors.New("failed to assert cached data as []Materia")
  }
 } else {
  // allMateria data is not in cache. Get from DB
  app.logger.Debug("cache miss")
  allMateria, err = app.db.GetAllMateria()
  app.setCache(string(ccmf.AllMateriaCacheKey), allMateria)
 }
 return
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  In-server cache
&lt;/h3&gt;

&lt;p&gt;We are using an in-server cache because:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The data fetched from the DB never changes.&lt;/li&gt;
&lt;li&gt;The same data is used by both \materia and \fusion endpoints.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Update &lt;code&gt;main.go&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// declare a cache and a mutex.
// the mutex is to ensure there is only one operation using the cache at a time.
type application struct {
 db     *database.DB
 logger *slog.Logger
 cache  map[string]interface{}
 wg     sync.WaitGroup
 mu     sync.Mutex
 config config
}

// in run() initialize the cache:
app := &amp;amp;application{
  config: cfg,
  db:     db,
  logger: logger,
  cache:  make(map[string]interface{}),
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update &lt;code&gt;api/helpers.go&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// remove backgroundTask()
// add getter and setter for the cache:

func (app *application) getCachedData(key string) (interface{}, bool) {
 app.mu.Lock()
 defer app.mu.Unlock()

 data, found := app.cache[key]
 return data, found
}

func (app *application) setCache(key string, value interface{}) {
 app.mu.Lock()
 defer app.mu.Unlock()

 app.cache[key] = value
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Handler for POST &lt;code&gt;\fusion&lt;/code&gt; in &lt;code&gt;api/handlers.go&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// showing only relevant parts of the code

func (app *application) fuseMateria(w http.ResponseWriter, r *http.Request) {
 var fusionReq MateriaFusionRequest
 err := request.DecodeJSON(w, r, &amp;amp;fusionReq)
 if err != nil {
  app.badRequest(w, r, err)
  return
 }

// Using the Validator we had defined in dtos.go
 fusionReq.Validator.CheckField(fusionReq.Materia1Name != "", "materia1name", "materia1name is required")
 fusionReq.Validator.CheckField(fusionReq.Materia2Name != "", "materia2name", "materia2name is required")
 fusionReq.Validator.CheckField(fusionReq.Materia1Mastered != nil, "materia1mastered", "materia1mastered is required")
 fusionReq.Validator.CheckField(fusionReq.Materia2Mastered != nil, "materia2mastered", "materia2mastered is required")

 if fusionReq.Validator.HasErrors() {
  app.failedValidation(w, r, fusionReq.Validator)
  return
 }

 var allMateria []ccmf.Materia

 allMateria, err = app.getAllMateriaFromApprSource()

 if err != nil {
  app.serverError(w, r, err)
 }

 var materia1Type string
 var materia1Grade int
 var materia2Type string
 var materia2Grade int

// matching the request input with the categories in DB
 for _, materia := range allMateria {
  if materia1Type != "" &amp;amp;&amp;amp; materia2Type != "" {
   break
  }
  if materia.Name == fusionReq.Materia1Name &amp;amp;&amp;amp; materia1Type == "" {
   materia1Type = materia.Type
   materia1Grade = materia.Grade
  }
  if materia.Name == fusionReq.Materia2Name &amp;amp;&amp;amp; materia2Type == "" {
   materia2Type = materia.Type
   materia2Grade = materia.Grade
  }
 }

 if materia1Type == "" || materia2Type == "" {
  app.badRequest(w, r, errors.New("one or both of the Materia names are not recognised"))
  return
 }

// game rule - higher grade Materia moves to first position
 exchangePositionsIfNeeded(&amp;amp;fusionReq, &amp;amp;materia1Grade, &amp;amp;materia2Grade, &amp;amp;materia1Type, &amp;amp;materia2Type)

 relevantBasicRuleMap := ccmf.BasicRuleMap[ccmf.MateriaType(materia1Type)]
 var relevantBasicRule ccmf.BasicCombinationRule

// finding the relevant combination rule
 for _, rule := range relevantBasicRuleMap {
  if (rule.FirstMateriaType == ccmf.MateriaType(materia1Type)) &amp;amp;&amp;amp;
   (rule.SecondMateriaType == ccmf.MateriaType(materia2Type)) {
   relevantBasicRule = rule
   break
  }
 }

 var resultantMateria MateriaDTO

// game rule - grade of resultant Materia depends on the input Materia as well as their Mastered state
 resultantMateriaGrade := determineGrade(fusionReq, materia1Grade)

 if relevantBasicRule.FirstMateriaType == "" {
  app.logger.Info("none of the basic rules satisfy the requirement.")

  // get final output using complex rules
  resultantMateria = useComplexRules(materia1Grade, materia2Grade, resultantMateriaGrade, materia1Type, materia2Type, *fusionReq.Materia1Mastered, *fusionReq.Materia2Mastered, &amp;amp;allMateria)
 } else {
  // get final output using basic rules
  resultantMateriaType := relevantBasicRule.ResultantMateriaType

  for _, materia := range allMateria {
   if materia.Grade == resultantMateriaGrade &amp;amp;&amp;amp; materia.Type == string(resultantMateriaType) {
    resultantMateria.Name = materia.Name
    resultantMateria.Type = materia.DisplayType
    resultantMateria.Description = materia.Description
    break
   }
  }
 }


 err = response.JSON(w, http.StatusOK, resultantMateria)
 if err != nil {
  app.serverError(w, r, err)
 }
}

// Combination rules which do not follow any pattern, and had to be coded separately
func useComplexRules(materia1Grade, materia2Grade, resultantMateriaGrade int, materia1Type, materia2Type string, materia1Mastered, materia2Mastered bool, allMateria *[]ccmf.Materia) (resultantMateria MateriaDTO) {
 var resultantMateriaType string

 switch {
 // Complex Rule 1: FIL, Defense
 case (materia1Type == string(ccmf.Fire) ||
  materia1Type == string(ccmf.Ice) ||
  materia1Type == string(ccmf.Lightning)) &amp;amp;&amp;amp; materia2Type == string(ccmf.Defense):
  if materia1Grade == 1 &amp;amp;&amp;amp; materia2Grade == 1 {
   // output is Defense when grades are equal to 1
   resultantMateriaType = string(ccmf.Defense)
   if materia1Mastered || materia2Mastered {
    // final Grade is increased when output is Defense
    increaseGrade(&amp;amp;resultantMateriaGrade)
   }
  } else {
   // output is FIL when grades are NOT equal to 1
   resultantMateriaType = materia1Type
  }

  ... 

// prepare response DTO
 updateResultantMateriaData(allMateria, resultantMateriaGrade, resultantMateriaType, &amp;amp;resultantMateria)
 return resultantMateria
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Complete handler code can be found &lt;a href="https://github.com/RayMathew/crisis-core-materia-fusion-api/blob/main/api/handlers.go" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Swagger UI and OpenAPI definition doc
&lt;/h3&gt;

&lt;p&gt;Add the Swagger library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go get -u github.com/swaggo/swag/cmd/swag
go get github.com/swaggo/http-swagger
go get github.com/swaggo/swag
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;routes.go&lt;/code&gt; uncomment the Swagger line, and add the import:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;httpSwagger "github.com/swaggo/http-swagger"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the handler, DTO and model files, add comments for Swagger documentation. Refer &lt;a href="https://github.com/swaggo/swag" rel="noopener noreferrer"&gt;this&lt;/a&gt; for all options.&lt;/p&gt;

&lt;p&gt;In the terminal, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd api
swag init -d .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates an &lt;code&gt;api/docs&lt;/code&gt; folder, with the definition available for Go, JSON and YAML.&lt;/p&gt;

&lt;p&gt;To test it, start the local server and open &lt;a href="http://localhost:4444/docs" rel="noopener noreferrer"&gt;http://localhost:4444/docs&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Final folder structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;📦 crisis-core-materia-fusion-api
.gitignore
├─ Dockerfile
├─ LICENSE
├─ Makefile
├─ README.md
├─ api
│  ├─ docs
│  │  ├─ docs.go
│  │  ├─ swagger.json
│  │  └─ swagger.yaml
│  ├─ dtos.go
│  ├─ errors.go
│  ├─ handlers.go
│  ├─ helpers.go
│  ├─ main.go
│  ├─ middleware.go
│  ├─ requests.go
│  ├─ routes.go
│  └─ server.go
├─ go.mod
├─ go.sum
└─ internal
   ├─ crisis-core-materia-fusion
   │  ├─ constants.go
   │  └─ models.go
   ├─ database -- db.go
   ├─ env -- env.go
   ├─ request -- json.go
   ├─ response -- json.go
   └─ validator
      ├─ helpers.go
      └─ validator.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  3. Setting up the remote PostgreSQL instance in CockroachDB
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Use the steps from &lt;a href="https://www.cockroachlabs.com/docs/stable/deploy-app-gcr" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;After creating the certificate, create &lt;code&gt;&amp;lt;rootfolder&amp;gt;/certs/root.crt&lt;/code&gt; in the project and add the certificate there. We will make a reference to this file later in the Google Run configuration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CAUTION!&lt;/strong&gt; We are &lt;strong&gt;NOT&lt;/strong&gt; pushing this folder to the remote repository. Add &lt;code&gt;certs/&lt;/code&gt; folder to &lt;code&gt;.gitignore&lt;/code&gt;. We are creating the certificate in local only to test the connection, if you wish.&lt;/li&gt;
&lt;li&gt;Now when you go to CockroachDB → Dashboard → Left Menu → Databases, you should be able to see the DB you created.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Data Migration
&lt;/h3&gt;

&lt;p&gt;From your local DB instance, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pg_dump --no-owner --no-privileges -U &amp;lt;admin_username&amp;gt; -d materiafusiondb &amp;gt; full_dump.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Go to CockroachDB → Left Menu → Migrations → Add Schema → Drag the SQL file you just got. All the steps will run, except the table data insertion. It will also show you a list of steps that were executed.&lt;/li&gt;
&lt;li&gt;At the time of writing this article, the PostgreSQL instance in CockroachDB doesn’t support statements like &lt;code&gt;IMPORT INTO&lt;/code&gt;. So I had to create an &lt;code&gt;INSERT&lt;/code&gt; statement in a local SQL file for 270 rows (which we can derive from the &lt;code&gt;pg_dump&lt;/code&gt; output we just got).&lt;/li&gt;
&lt;li&gt;Log into the remote instance, and run the SQL file.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Logging in to the remote instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;psql -h &amp;lt;REMOTE_DB_CLUSTER_HOSTNAME&amp;gt; -U &amp;lt;REMOTE_USERNAME&amp;gt; -d materiafusiondb -p &amp;lt;REMOTE_DB_PORT&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  4. Deploy a Google Cloud Run instance
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Create a Dockerfile like &lt;a href="https://github.com/RayMathew/crisis-core-materia-fusion-api/blob/main/Dockerfile" rel="noopener noreferrer"&gt;this&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Go to &lt;a href="https://console.cloud.google.com/run?hl=en" rel="noopener noreferrer"&gt;Google Cloud Run&lt;/a&gt; and create a new project for the API.&lt;/li&gt;
&lt;li&gt;Create Service → &lt;em&gt;Continuously deploy from a repo&lt;/em&gt; → &lt;em&gt;SETUP WITH CLOUD BUILD&lt;/em&gt; → &lt;em&gt;Repository Provider&lt;/em&gt; = Github → Select your repo → &lt;em&gt;Build Type&lt;/em&gt; = Dockerfile → Save.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Authentication&lt;/em&gt; = &lt;em&gt;Allow unathenitcated invocations&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Most of the defaults should be fine as is.&lt;/li&gt;
&lt;li&gt;Scroll down to Containers → &lt;em&gt;Container Port&lt;/em&gt; = 4444.&lt;/li&gt;
&lt;li&gt;Select &lt;em&gt;Variables and Secrets&lt;/em&gt; tab, and add the same environment variables as we have in our local &lt;code&gt;.env&lt;/code&gt; file.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Values:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;HTTP_PORT = 4444&lt;/li&gt;
&lt;li&gt;DB_DSN = &lt;code&gt;&amp;lt;remote_cockroachdb_url&amp;gt;?sslmode=verify-full&amp;amp;sslrootcert=/app/certs/root.crt&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;API_TIMEOUT_SECONDS = 5&lt;/li&gt;
&lt;li&gt;API_CALLS_ALLOWED_PER_SECOND = 1&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Using Google Secret Manager for the certificate
&lt;/h3&gt;

&lt;p&gt;The last piece of the puzzle.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Search for Secret Manager → Create Secret → Name = ‘DB_CERT’ → Upload the &lt;code&gt;.crt&lt;/code&gt; certificate of the CockroachDB.&lt;/li&gt;
&lt;li&gt;In Cloud Run → (your service) → Click &lt;em&gt;Edit Continuous Deployment&lt;/em&gt; → Scroll down to &lt;em&gt;Configuration&lt;/em&gt; → Open Editor.&lt;/li&gt;
&lt;li&gt;Add this as the first step:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  - name: 'gcr.io/google.com/cloudsdktool/cloud-sdk:slim'
    args:
      - '/bin/sh'
      - '-c'
      - &amp;gt;
        mkdir -p certs

        gcloud secrets versions access latest --secret="DB_CERT" &amp;gt;
        certs/root.crt
    id: Fetch Secret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will make Cloud Build create the file &lt;code&gt;certs/root.crt&lt;/code&gt; in our project before the build starts, so that the Dockerfile will have access to it even though we never pushed it to our Github repository.&lt;/p&gt;




&lt;p&gt;And that’s it. Try pushing a commit and check if the build triggers. The Cloud Run dashboard will show the URL of your hosted Go server.&lt;/p&gt;




&lt;p&gt;For questions related to “Why did you do X and not Y?” read &lt;a href="https://github.com/RayMathew/crisis-core-materia-fusion-api/discussions/3" rel="noopener noreferrer"&gt;this&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For anything else that you want to know or discuss, go &lt;a href="https://github.com/RayMathew/crisis-core-materia-fusion-api/discussions" rel="noopener noreferrer"&gt;here&lt;/a&gt;, or comment below.&lt;/p&gt;

</description>
      <category>go</category>
      <category>googlecloud</category>
      <category>cockroachdb</category>
      <category>postgres</category>
    </item>
    <item>
      <title>2023: A career review</title>
      <dc:creator>Ray Mathew</dc:creator>
      <pubDate>Tue, 14 May 2024 17:07:18 +0000</pubDate>
      <link>https://dev.to/raymathewdeveloper/2023-a-career-review-4nde</link>
      <guid>https://dev.to/raymathewdeveloper/2023-a-career-review-4nde</guid>
      <description>&lt;h2&gt;
  
  
  Milestones and moments:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Diversified into Data Mesh.&lt;/strong&gt; &lt;br&gt;
 Just as I had planned last year, I got into the theory of Data Mesh. It’s useful info for the future, but not something I’m able to utilize right now. I had no idea that the person who coined the term was a Thoughtworker! (&lt;a href="https://www.linkedin.com/in/zhamak-dehghani/" rel="noopener noreferrer"&gt;Zhamak Dehghani&lt;/a&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Delays in TL role approval.&lt;/strong&gt; &lt;br&gt;
 I had already been feeling hesitant about trying for the TL with the recent management changes in the client company. I think my own TL had convinced me to try once more, given that I had already put in so much work. So I did. But things didn't really move along for  at least 5 months.&lt;/p&gt;

&lt;p&gt;I was interviewed multiple times on the Thoughtworks side - the questions were fairly easy, mainly about what I would do in various technical and business related scenarios.&lt;/p&gt;

&lt;p&gt;I was also given me a formal feedback from the client, that I was the most capable in the team of taking up the TL role, but I needed to stop relying on my own TL to get me out of difficult spots. Admittedly, I felt exposed. I DID in fact do that with both my previous TLs.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Real world Kafka and Kubernetes experience.&lt;/strong&gt; &lt;br&gt;
 Small achievement in the big scale of things, but a big mental breakthrough for me. I had been intimidated of using big systems like Kafka and Kubernetes, because for so long they were just names brought up frequently, signifying their importance and abstraction of the systems we were working on. Not to mention they took me a while to learn. It was not the same concept as learning a language or React. &lt;br&gt;
But I can now say that I’ve worked on both Kafka and Kubernetes. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Unanimous support for TL role.&lt;/strong&gt; &lt;br&gt;
 All my teammates said I was ready to lead the team. My current TL said I was ready. My mentor, also a TL, said I was ready. &lt;/p&gt;

&lt;p&gt;So why was there a delay? I'm not sure...&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Finally became TL. Crap, now what?&lt;/strong&gt; &lt;br&gt;
 It finally happened. I had basically given up the thought of being TL and was looking to move on to another account. They had even gotten someone who was apparently supposed to be the new TL.&lt;/p&gt;

&lt;p&gt;But then it happened. I was told I was going to be the next TL during an in-person training session. I panicked. I had the responsibility of the team’s success or failure now. No one to fall back on. I should’ve maybe been celebrating more? It was a massive moment for my career.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Absolutely overloaded in the first few months.&lt;/strong&gt;&lt;br&gt;
 It’s the most I’ve ever worked in my life. I worked 9 continuous days (including Saturday and Sunday), before I felt a bit sick and had to stop. I was waking up at 6am, and working till 11pm. Getting requirements ready for the team, hashing out solutions with the other teams, creating contracts, etc. I experienced for the first time what it was like where I was the bottleneck of a team’s output, and everybody wanted my time. From the stakeholders to the Devs to the BA. People called me on the phone with no hesitation, assuming that I’m always available to answer their “one minute” question which would turn into 20 minutes. Devs would either be waiting for me to tell them what to do, or skipping code reviews because I looked busy. &lt;/p&gt;

&lt;p&gt;We got in serious trouble when, by just the 3rd Sprint, we had 16 points of spillover, and the client's upper management were asking what was wrong.&lt;/p&gt;

&lt;p&gt;For the first time in my life, I became that guy who ignored calls. I had to. I also ignored unplanned Zoom meetings. I couldn’t be the “helpful guy” to everyone anymore, except my own team. They were the first priority. I decided that the BA and IM alone would go to meetings to clarify requirements, and I would join when things got technical. After all, that should’ve been the extent of my role. But Thoughtworks always expected the TLs to be Jacks of all trades. &lt;/p&gt;

&lt;p&gt;Things got maybe 5% percent less stressful by taking the above measures. But I was still working a hell lot, I hardly exercised, I lost weight, and I must’ve worked at least 9 Sundays in 6 months.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Got to implement my strengths - not tech, but people skills.&lt;/strong&gt; &lt;br&gt;
 1) I managed to make the team meet the clients upper management. I believed it was very important, for the team to feel a sense of ownership, to put a face on the people who were telling them what to do for 10 hours a day, 50 weeks in a year. I was the only one who even gave it any importance.&lt;br&gt;
 2) I made a huge bit of progress in motivating one my team members to be more proactive and take up solo tasks.&lt;br&gt;
 3) I convinced one of my mentee’s TL to not give her a “Needs Improvement” rating, as it would demotivate her. It took a full 30 minutes. Some of the most difficult talking and negotiation I’ve ever done.]&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The delegation problem.&lt;/strong&gt;&lt;br&gt;
 This was my biggest failure as a team lead. To delegate, and to make the team more autonomous. &lt;br&gt;
The reason why I was such a bottleneck for the team was that they were always waiting on me to tell them what to do next. And when they did do something, they would miss some of the impacts it had on other parts of code. How does a team lead know the answer to this question -  was the bottleneck failure due to him / her not empowering the team more and preparing more clear instructions in advance? Or was it because the team was not mature / interested enough to take ownership, and initiative? Is being in charge of hiring the only solution?&lt;/p&gt;

&lt;p&gt;I don’t yet have an answer to any of these questions today.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Some difficulties and learnings.&lt;/strong&gt;&lt;br&gt;
 I’ll try to keep this brief.&lt;/p&gt;

&lt;p&gt;1) Every requirement was urgent. So, nothing was. We had to make our own assessment of what needed to be delivered first.&lt;/p&gt;

&lt;p&gt;2) Unreasonable deadlines and expectations. For example, one of the folks I reported to expected me to write the pseudocode, error codes, error msgs, FSM diagram etc for the entire user journey in a week, so that the team had a template to follow. The actual feature ended up taking 6 months and 8 developers. &lt;/p&gt;

&lt;p&gt;3) Office politics. For example, there was a guy who frequently lied to get his way, and his superiors turned a blind eye. There was a stakeholder who didn't understand tech, but came into meetings claiming that I should've caught some bug before it happened. There was a team who claimed that we would do "consumer driven development", so that my team, the consumers, had the responsibility of creating contracts. But when we wanted to make critical changes later on, they refused, claiming that they cannot alter their internal systems for our benefit.&lt;/p&gt;

&lt;p&gt;Managing all of these and more required a lot of discussions, meetings, and heated arguments. It was not at all what I expected when I was starting my career as a developer.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;In the end, it was a success.&lt;/strong&gt;&lt;br&gt;
  Despite all these headwinds, we did it. We went into CUG testing early December, with very few bugs, most of which were a data issue. &lt;/p&gt;

&lt;p&gt;We. Did. It. The nightmare that I endured to my mind and body was worth it. I could tell people for the rest of my life and my career, that I was TL for the first time in the most high profile and difficult TW project, and we delivered a product that the client was happy with. I got praise for the amount of workload and coordination I took on myself. I got praise from my team, including the very touching “Best TL I’ve worked with”.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Zero interviews.&lt;/strong&gt;&lt;br&gt;
  Nothing to add here. I just didn’t have the time to take out 2 hours of my day to interview someone when I was expected to deliver a feature before the requirements were ready.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;


&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Notable firsts:&lt;/strong&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;I became TL.&lt;/li&gt;
&lt;li&gt;I helped take a feature live as TL.&lt;/li&gt;
&lt;li&gt;Diversified into Data Mesh, got practical experience in Kafka and Kubernetes.&lt;/li&gt;
&lt;/ol&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;New things learnt:&lt;/strong&gt;
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Practical implementation of Finite State Machine in Backend for a user journey.&lt;/li&gt;
&lt;li&gt;Data Mesh.&lt;/li&gt;
&lt;li&gt;Managing clients.&lt;/li&gt;
&lt;li&gt;Managing a Dev and QA team.&lt;/li&gt;
&lt;li&gt;Golang (strengthening concepts).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A very small list this time.&lt;/p&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;Reflections:&lt;/strong&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Being a TL for the first time with TW India’s most difficult client, with their most high profile feature, was harrowing. I lost sleep, weight, and any semblance of a personal life and leisure activities. I felt the urge to quit within just a month. But I persevered, and thankfully it was worth it. For the rest of my life I’ll be able to carry the confidence that if I could do something that difficult, I’m a lot more capable than I give myself credit for. Not to mention, we took the feature live, with no major bugs in CUG or Prod.&lt;/li&gt;
&lt;li&gt;I’m glad I was able secure my exit after just 7 months in that project. It was terrible for my health, and no one should ever have to work like that. &lt;/li&gt;
&lt;li&gt;The moment you climb the ladder and start managing people, office politics and mind games become visible. It’s something everyone unfortunately needs to learn to handle, so that they’re not taken advantage of.&lt;/li&gt;
&lt;li&gt;It may not have occupied a lot of my time or effort, but being firm with teammates who you used to be your peers and friends is one of the most difficult things to do.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;My plan for 2024: Relax. I’m going on a government imposed sabbatical, since I need a work permit once I enter US on an H4 visa. Also, learn things outside of my client's requirements for the first time in a while, and then look for a new job.&lt;/p&gt;

</description>
      <category>yearinreview</category>
      <category>2023</category>
    </item>
    <item>
      <title>2022: A career review</title>
      <dc:creator>Ray Mathew</dc:creator>
      <pubDate>Wed, 21 Dec 2022 11:08:33 +0000</pubDate>
      <link>https://dev.to/raymathewdeveloper/2022-a-career-review-lc3</link>
      <guid>https://dev.to/raymathewdeveloper/2022-a-career-review-lc3</guid>
      <description>&lt;h2&gt;
  
  
  Milestones and moments:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Completed my temp co-Tech Lead reign.&lt;/strong&gt; &lt;br&gt;
 2 weeks of being a temp co-TL along with a teammate ended on 7th Jan. I was ashamed at the time to admit to myself that he had done a better job than I had. He could’ve probably even managed on his own. I kept zoning out, unable to track the 20+ problems or provide good solutions. My speaking skills managed to salvage some points.&lt;br&gt;
I also didn’t like the experience I just had as co-TL. It was too much responsibility; I couldn’t take a break for a minute. What a reality check it was to my ambitions. But I didn’t want to tell this to my mentor and disappoint him.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Moved to a new sub-squad to assist the new TL.&lt;/strong&gt; &lt;br&gt;
 A 3rd sub-squad in 2 years, with a new TL. I was initially in the running to be the TL myself, but the management team (rightly, in my opinion) hired someone new to take over. And I was to help him to settle in to the Thoughtworks (TW) and the project's ways of working. My mentor said that this was a good opportunity for me to have increased levels of responsibilities, and I had to agree he was right. But it also meant I would no longer be working with him. It was the end of a great period for me, and a move on to something more challenging.&lt;br&gt;
I did not like my new role in the initial few weeks. I was spending more time in meetings with the TL and the clients than writing any code. I felt like a fraud. I was earning a chunk of money to just talk and listen. &lt;br&gt;
I was also sometimes being called upon instead of the new TL to help deal with problems that required a historical context. I was too busy sulking at the time to realise that this was a great boost to my reputation as a lead.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A heavy dose of architecture.&lt;/strong&gt;&lt;br&gt;
 I watched over 140 &lt;a href="https://www.developertoarchitect.com/" rel="noopener noreferrer"&gt;Developer to Architect&lt;/a&gt; videos over the year, and had subscribed to newsletters to keep up with the latest tools that an architect would need. This was the second major step (after becoming the stand-in TL) towards something bigger than writing code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Gave an internal and an external talk.&lt;/strong&gt; &lt;br&gt;
 I noticed my nervousness was much less than it had been in 2020. Another one of my goals (speaking) was progressing well.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;I am finally part of a team that took a feature live&lt;/strong&gt;. &lt;br&gt;
 It’s been 8 years, and I had previously always left a team just before the feature / product was released. And this time I just so happened to be the temp TL when it happened. A great boost to my confidence and my credibility as a future leader.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;I helped take a microsite to production.&lt;/strong&gt; &lt;br&gt;
 My biggest achievement this year. My TL encouraged me to lead the efforts in getting a microsite built and deployed all the way till production. This involved co-ordinating with multiple teams, keeping the clients updated, clarifying issues for other developers, and debugging in prod. Basically, being a team lead for a feature.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Took sole charge of debugging prod issues after the initial month of release.&lt;/strong&gt; &lt;br&gt;
  Again, a bit of luck. The TL was not available. I was reading the prod logs alone, and reporting on them. This led to the client reaching out to me directly for subsequent issues.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The team has started to consider me as a lead&lt;/strong&gt;. &lt;br&gt;
  I had been acting as a substitute TL for many months now whenever the TL was not around. But the day I started explaining our Sprint plans to the team was the day they finally accepted that I too was a capable lead in his absence. I hadn’t been looking for their approval, but it boosted my confidence none the less that I was doing something right.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Went to office for the first time since the pandemic started on 26th Aug, 2022&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;I now find the role of a mentor easy&lt;/strong&gt;. &lt;br&gt;
  As of the end of the year, I’m working with 9 mentees (one of the highest in TW India) and have gotten good reviews from them.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Interviews on hold&lt;/strong&gt;. &lt;br&gt;
  I conducted very few interviews this year, due to the pressure of our delivery.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;


&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Notable firsts:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Was part of a team that took a product live.&lt;/li&gt;
&lt;li&gt;Led the efforts in getting a feature built and deployed to prod.&lt;/li&gt;
&lt;li&gt;Acted as temp TL consistently throughout the year.&lt;/li&gt;
&lt;li&gt;Involved in the planning stages of the team’s work.&lt;/li&gt;
&lt;/ol&gt;






&lt;h3&gt;
  
  
  New things learnt:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;nmap (basics)&lt;/li&gt;
&lt;li&gt;BDD testing, using ginkgo and gomega&lt;/li&gt;
&lt;li&gt;Distributed logging, via streaming and subscribing&lt;/li&gt;
&lt;li&gt;Fitness functions&lt;/li&gt;
&lt;li&gt;Observability&lt;/li&gt;
&lt;li&gt;Kubernetes (basics)&lt;/li&gt;
&lt;li&gt;Monorepos&lt;/li&gt;
&lt;li&gt;GoCD configuration&lt;/li&gt;
&lt;li&gt;Webpack configuration&lt;/li&gt;
&lt;li&gt;Web 3.0&lt;/li&gt;
&lt;li&gt;React Native Deeplinking (basics)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://json-schema.org/" rel="noopener noreferrer"&gt;https://json-schema.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Git worktrees, git squash.&lt;/li&gt;
&lt;li&gt;CryptoJS.&lt;/li&gt;
&lt;li&gt;Websockets.&lt;/li&gt;
&lt;/ol&gt;






&lt;h2&gt;
  
  
  Reflections:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;There were 3 major themes this year: learning architecture, getting face-time and trust with clients, and leading the efforts in building a feature.&lt;/li&gt;
&lt;li&gt;I have completely conquered one of my weaknesses from last year - context switching. It was achieved by sheer brute force practice.&lt;/li&gt;
&lt;li&gt;I had disliked the taste of the TL role at the start of the year - the messiness and the chaos. I just wanted to write code. But by the end of the year I realised that building systems is inherently a messy process and it’s a marvel anything works at all. Only a conscientious person can get the job done. I am now okay with what the role requires.&lt;/li&gt;
&lt;li&gt;No matter how much we talk about values and culture, money matters most. And one must be wary of moonshot projects during recession.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;My plan for 2023: Non-tech skills of a leader, and Data. It’s time to diversify my skillset.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>webdev</category>
      <category>gamedev</category>
      <category>marketing</category>
    </item>
    <item>
      <title>2021: A career review</title>
      <dc:creator>Ray Mathew</dc:creator>
      <pubDate>Sat, 29 Jan 2022 07:25:23 +0000</pubDate>
      <link>https://dev.to/raymathewdeveloper/2021-a-career-review-5d34</link>
      <guid>https://dev.to/raymathewdeveloper/2021-a-career-review-5d34</guid>
      <description>&lt;p&gt;Continuing the tradition I started last year (29 days late).&lt;/p&gt;

&lt;h2&gt;
  
  
  Milestones and noteworthy moments:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Finally started giving showcases to clients.&lt;/strong&gt; &lt;br&gt;
 Despite being in a project which follows Agile Methodology for a year I had not yet had the opportunity. I have now done it several times, and been almost always appreciated. And yet, the stage fright makes me reluctant on the next occasion.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Gave 4 talks in 4 months.&lt;/strong&gt;&lt;br&gt;
I improved on the streak from the previous year, and made myself even more comfortable with giving talks than ever before. But I still have a long way to go before I feel truly at ease on stage (virtual or otherwise). By the time I had given 3 talks I had wanted to take the rest of the year off from the palpitating pressure of doing any more.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Became obsessed with work in the beginning of the year.&lt;/strong&gt;&lt;br&gt;
I worked in 3 teams simultaneously for the first 3 months. I don’t know if it was because I wanted to ride the high of recent good reviews, or because I was using work as a welcome distraction from dealing with personal problems. Probably both. I worked for at least 11 hours on weekdays and prepped for the next week on weekends. There wasn't a moment for aimless thoughts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Had a performance review that was akin to a therapy session.&lt;/strong&gt;&lt;br&gt;
The review I had in February with the Office Principal(OP), People Champion(PC) and my mentors will probably never happen again, since Thoughtworks is expanding rapidly and formalising procedures. It involved me talking freely about my journey over the last 1.2 years, my mentors saying nice things about me, and concluding with the OP and PC giving advice on how to carry on in the future. There was no judgement, or even a business-like agenda to the meeting. I left the meeting feeling lighter and happier.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Started conducting interviews.&lt;/strong&gt;&lt;br&gt;
Being an interviewer is a position of privilege and power. It has to be treated very carefully, as you hold another person’s career in your hands in that moment. It’s also about trust from the company, as your decision could make the difference between a mediocre and a star employee being hired that could have ripple effects years into the future. &lt;br&gt;
Listening to the candidates talk about their work and projects also expanded my own knowledge. I loved doing it, despite it taking a lot of energy to listen carefully and ask calibrated questions for 1.5 hours.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Became a mentor and 'catalyst' to several people.&lt;/strong&gt;&lt;br&gt;
Again, a privilege and an honour. To put things in perspective, there was a time when I used to introduce myself to senior developers with "I'm sorry, I don't have a Computer Science background. Could you tell me what to do?". And now I give advice like I actually have something worthwhile to say. Surreal.&lt;br&gt;
I have wondered many times though, if I had done enough to help my mentees, or if I could have done more. They themselves might not know the full answer to that question. How can one tell? How does one distinguish between a person who wants to try things on their own versus a person who does not want to be a burden by asking for more help?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Became obsessed with improving efficiency of my team in the middle of the year.&lt;/strong&gt;&lt;br&gt;
The '1% improvement everyday' idea had taken a hold on me. I was constantly looking for the smallest adjustments not just in our codebase, but in our team as well. For example: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Learnings were to be shared with everyone so that no one is reinventing the wheel.&lt;/li&gt;
&lt;li&gt;Speed feedback sessions to find everyone's blind spots.&lt;/li&gt;
&lt;li&gt;Creating docs for standard procedures so that no one wastes time asking how to do something twice.&lt;/li&gt;
&lt;li&gt;Command aliases that automated several steps in our daily work.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Acted as a shadow trainer.&lt;/strong&gt;&lt;br&gt;
Yet some more luck, where I was named a shadow trainer for a workshop because all the student slots had already been taken up. This lead to opportunities where I could’ve become a permanent trainer as well.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Wrote an article after ages&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://dev.to/raymathewdeveloper/my-ambition-has-evolved-beyond-being-a-technical-expert-44m"&gt;My Ambition Has Evolved Beyond Being a Technical Expert&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Learnt a little Golang and made my first steps towards being a full stack dev.&lt;/strong&gt;&lt;br&gt;
Worked on a couple of Stories, some refactoring, and fixed bugs. This has been a very important step towards me becoming a full stack developer, and making me more eligible to be a Tech Lead in the future. My own Tech Lead has been incredibly patient - he walked me through the solutions himself instead of pairing me off with another developer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;IPO.&lt;/strong&gt;&lt;br&gt;
I was in the right place at the right time with the Thoughtworks IPO. Being part of an IPO was a bucket list item, and I had never expected it to be fulfilled so soon.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Temporary co-Tech Lead.&lt;/strong&gt;&lt;br&gt;
A teammate and I were made temporary co-TLs of our team for 3 weeks. The word 'privilege' is starting to lose meaning by now. It was a massive learning curve, and one I need to work more on - particularly context switching.&lt;br&gt;
&lt;br&gt;&lt;/p&gt;


&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Notable firsts:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Worked as a judge in a competition (Technovation Girls).&lt;/li&gt;
&lt;li&gt;Became a mentor.&lt;/li&gt;
&lt;li&gt;Gave 4 talks in a year.&lt;/li&gt;
&lt;li&gt;Conducted interviews, and learnt to anchor them.&lt;/li&gt;
&lt;li&gt;Worked on backend tasks again for the first time in 6 years.&lt;/li&gt;
&lt;li&gt;Named temporary co-Tech Lead.&lt;/li&gt;
&lt;/ol&gt;






&lt;h3&gt;
  
  
  New things learnt:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Add git hooks to a project, using husky.&lt;/li&gt;
&lt;li&gt;Configuring prettier and eslint config files.&lt;/li&gt;
&lt;li&gt;Dealing with env variables and processes using NodeJS.&lt;/li&gt;
&lt;li&gt;Learnt how to make the console pretty with NodeJS.&lt;/li&gt;
&lt;li&gt;git stash (in depth), recursive modules and general commands in depth (&lt;a href="https://www.notion.so/Git-commands-b95cff3313044f369d2964843fd56eb0" rel="noopener noreferrer"&gt;link&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Docker (re-learnt it after forgetting much of what I learnt last year). Made extensive notes on it (&lt;a href="https://raymathew.notion.site/Docker-in-Depth-06444c6883db481ea713b3b2140ac3c0" rel="noopener noreferrer"&gt;link&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Integration of Google Forms, Google Sheets, App Scripts, and email automation.&lt;/li&gt;
&lt;li&gt;Redux.&lt;/li&gt;
&lt;li&gt;Using burp suite to monitor network calls in an Android emulator.&lt;/li&gt;
&lt;li&gt;React Native localisation.&lt;/li&gt;
&lt;li&gt;Background tasks in React Native.&lt;/li&gt;
&lt;li&gt;Golang (basics)&lt;/li&gt;
&lt;li&gt;Bash (basics)&lt;/li&gt;
&lt;li&gt;Vim (basics)&lt;/li&gt;
&lt;li&gt;Python (relearnt after losing touch).&lt;/li&gt;
&lt;li&gt;Jupyter, Pandas and matplotlib.&lt;/li&gt;
&lt;li&gt;Contract testing.&lt;/li&gt;
&lt;/ol&gt;






&lt;h2&gt;
  
  
  Reflections:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Your career (and your life) is the sum of luck, interacting with the right people, grabbing opportunities, and grinding out results. And the compounding of all of these over time. I realised it the most this year because of examples such as these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Working with a Tech Lead who not only is monumentally knowledgeable, but also gives many opportunities to his team members to shine.&lt;/li&gt;
&lt;li&gt;Working in a company that heavily emphasises on giving feedback, so that everyone is feeling good and self aware throughout the year.&lt;/li&gt;
&lt;li&gt;Working with a lot of smart people makes you look good too.&lt;/li&gt;
&lt;li&gt;Saying yes to one minor opportunity in the beginning of the year lead to my name being heard among some of the biggest folk in the company by the end of the year.&lt;/li&gt;
&lt;li&gt;My performance review was coincidentally postponed to after getting a word of praise from a client. So I knew in advance my review would go well.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ironically, despite being a mere spectator to much of the success that you get, you still won’t get much success if you don’t work hard when it matters.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The power of saying yes to opportunities.&lt;/strong&gt;&lt;br&gt;
It lead to one benefit after another. Joining a volunteer group led me to learning about Google App Scripts and email automation. The work done with that team later led me to joining a global level team and meeting a few veteran TWers. It also got me working closely with an India-wide program manager, which gave me little bonuses like being featured in a newsletter and receiving great reviews.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Importance of appreciation.&lt;/strong&gt;&lt;br&gt;
TW brought the culture of giving appreciation in a big way this year, by building an internal application to express gratitude publicly to each other. For added measure, they gamified the experience to ensure maximum participation. Heartfelt messages that would’ve otherwise never been sent were pouring in from every direction. And the after-effect was a lot of people feeling good about themselves and their teammates, and happy to come to work the next day.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The power of being reliable.&lt;/strong&gt;&lt;br&gt;
Being the smartest person in the room can only take you so far (and my ego has been made painfully aware again this year that I’m not the one). It loses its value when you are not prepared to grind out results by any means imaginable. &lt;br&gt;
In an industry where there is always more work than can ever be done, delivering on what you promised - being reliable - is gold.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;My plan for 2022: DevOps, backend development and improving context switching as a team lead.&lt;/p&gt;

</description>
      <category>yearinreview</category>
      <category>2021</category>
    </item>
    <item>
      <title>My ambition has evolved beyond being a technical 'expert'</title>
      <dc:creator>Ray Mathew</dc:creator>
      <pubDate>Sat, 07 Aug 2021 16:47:15 +0000</pubDate>
      <link>https://dev.to/raymathewdeveloper/my-ambition-has-evolved-beyond-being-a-technical-expert-44m</link>
      <guid>https://dev.to/raymathewdeveloper/my-ambition-has-evolved-beyond-being-a-technical-expert-44m</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgflip.com%2F5ivy1l.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgflip.com%2F5ivy1l.jpg" alt="You must choose" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Being in Thoughtworks, a rare company that treats its developers like human beings, has given me the luxury of stepping back from the fierce race of constantly learning the latest framework, reading its 100 page documentation, and discovering its intricacies in StackOverflow answers. Thoughtworks makes it very clear to developers that they are appreciated and encouraged to do a lot more than just write code to help fulfil the company's promises to clients. Developers are told to be leaders, manage stakeholder expectations, have expertise in security, conduct workshops for other developers, anchor communities, give talks in external platforms to promote the TW brand, work in social change projects that do not earn revenue, and so on.&lt;/p&gt;

&lt;p&gt;This directive from TW has made me ask myself for the first time "Do I really want to be the guy who has in depth knowledge and years of experience in, say, frontend JavaScript? Do I really want to be known as the go-to guy for technical expertise in things like CSS,  and hidden gems buried deep in a single line of OJET documentation?" My answer is No. And the clarity of my answer does not solely spawn from TW's support, but also things I learnt recently about product delivery, and the importance of doing things that matter in the larger scheme of life.&lt;/p&gt;




&lt;h2&gt;
  
  
  Product delivery
&lt;/h2&gt;

&lt;p&gt;Product delivery has been broken down into a scientific and management endeavour over the last few decades. Research has been done, books have been written, conferences have been held, spin-off ideas have been spawned in multiple industries, and experts have arisen. And yet there still remains a creative aspect to it. You need to solve problems on the fly. You need to have people skills to notice which team members are producing poor results due to confidence issues rather than technical ineptitude. You need to focus on the most important problem when there are multiple fires you need to put out. For example, I've been having an itch for the last 3 months to write code to automate tracking of our Jira stories when a build reaches our QA environment, and send an alert to our Teams' group chat. Similar to Slack's Github bot. But I know that the more important problem is finding out why our automation environment sometimes has 17 random failures before succeeding, delaying our pipeline by 5+ hours. So I haven't scratched the itch yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why we do what we do
&lt;/h2&gt;

&lt;p&gt;Also, not enough people are able to connect the dots between what they're tasked and the larger goal of delivering a product that clients are happy with. If all developers only focused on technical tasks, while only the Technical Lead, Business Analyst, Manager and Quality Analyst focused on the business and functional logic, we're guaranteed to have a lot more bugs in our code. We're also guaranteed to miss our deadlines often - which affects our reputation, the client's bottom line, and our chances of getting more projects. Not enough people realise this. I do, and I care a lot about getting it right. It would be painful if a team of 100+ developers worked on a project for 1+ years, heroic moments were witnessed, all nighters were pulled, technical legends were praised, and at the 12th hour the client's end users saw a 502 error because there weren't enough integration tests.&lt;/p&gt;

&lt;p&gt;As long as I'm able to contribute by helping more of my teammates realise this, I'm happy to step back from trying to be a technical wizard. I've anyway noticed that I'm not able to come up with clever bits of logic as fast as some teammates. I'll leave them to it instead of having a meaningless competition.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclaimer: I'm a complete proponent of doing things that have no value other than for the giggles. Just not when it comes to work.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's the point anyway?
&lt;/h2&gt;

&lt;p&gt;And finally, I've realised the importance of only doing things that will make a difference in people's lives. The world has so many problems, most of which cannot be solved in one lifetime. Add to it that there are millions of people creating more problems everyday, and it can feel like a suffocating, losing battle. In the midst of all this, what value are we adding for anyone, including our own, when we spend time on problems that don't make a difference? You may be busy, you may be seen as hardworking. But you are not making a difference in people's lives. &lt;/p&gt;

&lt;p&gt;I'd rather be anyone but that person.&lt;/p&gt;

</description>
      <category>reflection</category>
    </item>
    <item>
      <title>2020: A career review</title>
      <dc:creator>Ray Mathew</dc:creator>
      <pubDate>Wed, 30 Dec 2020 09:24:30 +0000</pubDate>
      <link>https://dev.to/raymathewdeveloper/2020-a-career-review-4da9</link>
      <guid>https://dev.to/raymathewdeveloper/2020-a-career-review-4da9</guid>
      <description>&lt;h3&gt;
  
  
  Why am I bothering to write this?
&lt;/h3&gt;

&lt;p&gt;I've been a software developer for close to six years now, but this is the first time I'm writing a review about the year gone by. Or writing anything at all related to my career. Many reasons prompted this change:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;This has been a breakthrough year in multiple ways. 

&lt;ul&gt;
&lt;li&gt;My knowledge tree widened beyond what I thought was possible in such a short period, and yet the effort expended was not excruciating. In other words, the effects of compounding on previous knowledge have become as significant as acquiring more knowledge. &lt;/li&gt;
&lt;li&gt;I thought the learning curve I was on till the end of 2019 was roughly at a 60 degree angle - taking my work seriously, studying on weekends, solving others' problems on StackOverflow, and the occasional all-nighter. But when I started my new job at ThoughtWorks (TW) and saw how much people knew and learnt everyday, I realised my curve was closer to 20 degree. It really does make a massive difference when you surround yourself with the right people.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;I realised this year the value of keeping records of every little detail - both professional and personal. The mind is truly a horrible hard drive; it retains only those memories which have strong emotions associated with them. This year's strongest emotional connection to work stuff are the two internal talks I gave. The stage fright was tantamount to mild panic attacks.&lt;/li&gt;
&lt;li&gt;I've made a small adjustment in my career path due to the things I've learnt this year (explained below). I want to look back at this moment when I make my next adjustment.&lt;/li&gt;
&lt;li&gt;I realised I would enjoy reading these kind of posts years later.&lt;/li&gt;
&lt;li&gt;Unexpected benefits. If you expose your work to the outside world, sooner or later someone comes forward with a great opportunity, insight, or correction for you.&lt;/li&gt;
&lt;/ol&gt;






&lt;h2&gt;
  
  
  Milestones and unique moments:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Got a new job at the beginning of the year.&lt;/strong&gt; &lt;br&gt;
It's been surreal so far. I had no idea smaller companies of cultures similar to FAANG existed in India. I feel fortunate to have landed here, and bizarre that they reached out to me for an interview. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Went into a mental slump and loss of motivation due to my new salary and seeing younger people knowing much more than me.&lt;/strong&gt;&lt;br&gt;
I had been working for three years to make a big jump. At office I used to get my own work done and help my colleagues as well. Sometimes I would help up to four people at a time. On many days I would wake up at 5 A.M. and start answering Stackoverflow questions without freshening up. I worked on FreeCodeCamp projects and posted them on Github. I posted code snippets I was proud of on Codepen. I learnt design basics and CSS in depth, to became the 'go-to guy' for styling in my team. I wrote code to solve my friends' and my personal problems (Python scripts).&lt;/p&gt;

&lt;p&gt;But, when I started my new job two things happened: 1. The three year goal I had been working on was finally complete. I felt empty and devoid of purpose. 2. I thought I was fairly knowledgeable after all the hard work I had done over the last 3 years  - that thought shattered when I met my new younger colleagues.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Turbo-charged my note-taking and started tracking all aspects of work and personal life with &lt;a href="https://www.notion.so/product" rel="noopener noreferrer"&gt;Notion&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Gave 2 talks.&lt;/strong&gt;&lt;br&gt;
I gave 2 internal talks within the span of 4 months. My previous record had been to give 1 talk roughly every 3 years. The company's culture of sharing knowledge was definitely the biggest factor that pushed me to do this.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Stepped into security for the first time&lt;/strong&gt;.&lt;br&gt;
Last year, I had made a plan that I needed to have sufficient knowledge in all areas - backend, frontend, devops, security, delivery, finding clients, etc. before I could think about being my own boss years in the future. This year, I took the first, most difficult and important step by starting to learn about web application security. Why difficult? Simply because I had a mental block that it was difficult and a black box unlike any other.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;First ever responsibility that spanned multiple teams - facilitating security assessments.&lt;/strong&gt;&lt;br&gt;
This milestone illustrates better than any other the theme of my transition from my old company to new, as well as my transition into a senior developer. In my old company my work was limited to creating UI pages, and I interacted only with the lead developer and manager. Facilitating security assessments, however, was my first ever task that involved multiple teams across the company. It was also my first foray into security. Interesting to note: these kind of roles are very common at TW - you can volunteer for them the moment you join as a college graduate. TW truly breeds leaders.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Read &lt;a href="https://amzn.to/3rEKqMf" rel="noopener noreferrer"&gt;Extreme Programming Explained&lt;/a&gt; and &lt;a href="https://amzn.to/3pAR3xe" rel="noopener noreferrer"&gt;The Phoenix Project&lt;/a&gt;, which massively affected my thoughts of what it means to be a developer, a team player, a consultant.&lt;/strong&gt;&lt;br&gt;
Extreme Programming taught me how to think like an agile team member, and contribute towards the common goal. It is a well-written book, and I would find myself wanting to improve things right away in my team every 10 pages. The Phoenix Project taught me to think even bigger - the goal of a company, the importance of a smoothly running pipeline, and the illusion of progress when you use bad benchmarks. I hadn't read many software-related books before these two. I'm excited and in awe of how much more knowledge I could gain in the future from similar books.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Switched to a MacBook after a lifetime of Windows.&lt;/strong&gt;&lt;br&gt;
What a change. I don't ever want to switch back. Except for maybe playing games.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Turned from a UI specialist to a generalist.&lt;/strong&gt; &lt;br&gt;
Partly my decision, and partly from the requirements of TW, I have decided to spend time learning backend languages as well.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Realized I wanted to be a big-picture person like Elon Musk, and not a specialist like Tesla's head of AI. Also, I value the importance of hiring the right people and having the right culture.&lt;/strong&gt;&lt;br&gt;
I'm not sure where exactly this change originated from. Maybe from reading the above two books, maybe from seeing how much my colleagues can go down the rabbit hole (spending 8 months trying to get one pull request accepted), maybe from seeing how my health starts to get affected when I delve deep into topics. Or maybe from realising more than ever that all your special skills are worthless if you're not providing value to the customer. I think the last one is the most important. Providing value can be done through simple things as well, and doesn't always have to involve in-depth knowledge. Sometimes it could be as simple as making old ideas better.&lt;/p&gt;

&lt;p&gt;Also, from observing the culture of TW and the interview process (which includes questions about one's views on social issues), I realised how important it is to hire the right set of people in order to maintain high standards in a company.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;






&lt;h2&gt;
  
  
  Notable firsts:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Wrote my first co-authored &lt;a href="https://medium.com/@maniksrr/the-impact-of-anti-patterns-that-we-faced-as-a-software-development-team-ad371fd26997" rel="noopener noreferrer"&gt;post&lt;/a&gt;. &lt;/li&gt;
&lt;li&gt;Wrote my first coding related blog post (this).&lt;/li&gt;
&lt;li&gt;Used a web security testing tool (Burp Suite).&lt;/li&gt;
&lt;li&gt;Started solving web security lab problems.&lt;/li&gt;
&lt;li&gt;First ever responsibility that spanned multiple teams - facilitating security assessments.&lt;/li&gt;
&lt;li&gt;Attended an online conference (NodeJS).&lt;/li&gt;
&lt;li&gt;Gave 2 talks in a year.&lt;/li&gt;
&lt;/ul&gt;






&lt;h3&gt;
  
  
  New things learnt (unless stated otherwise, I learnt just enough to get work done. I can't be called the 'go-to guy' for any of these):
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;Kubernetes&lt;/li&gt;
&lt;li&gt;ES6, ES2020&lt;/li&gt;
&lt;li&gt;React&lt;/li&gt;
&lt;li&gt;React Native&lt;/li&gt;
&lt;li&gt;React performance tips from Kent C. Dodds&lt;/li&gt;
&lt;li&gt;Chrome snippets&lt;/li&gt;
&lt;li&gt;git rebase, filter-branch, stash, revert, gitignore_global&lt;/li&gt;
&lt;li&gt;yarn&lt;/li&gt;
&lt;li&gt;Jest, react-testing-library&lt;/li&gt;
&lt;li&gt;Nginx&lt;/li&gt;
&lt;li&gt;A ton of new terms, like canary testing, regression testing, smoke test, TDD, hallway usability testing, contract testing, broken window syndrome, desk check, Iteration Planning Meeting, timebox, spike.&lt;/li&gt;
&lt;li&gt;Webpack&lt;/li&gt;
&lt;li&gt;PWA&lt;/li&gt;
&lt;li&gt;Testing for memory leaks in NodeJS.&lt;/li&gt;
&lt;li&gt;How JS manages memory in the browser.&lt;/li&gt;
&lt;li&gt;Log aggregation&lt;/li&gt;
&lt;li&gt;CURL&lt;/li&gt;
&lt;li&gt;Memoization&lt;/li&gt;
&lt;li&gt;Tree shaking&lt;/li&gt;
&lt;li&gt;How to make the browser and CDN cache data.&lt;/li&gt;
&lt;li&gt;State machines&lt;/li&gt;
&lt;li&gt;Web application security&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It's never happened before that I do not have complaints for a full year. I truly couldn't have done anything better given the circumstances I was in, and not having the benefit of foresight.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;My plan for 2021&lt;/strong&gt;: Continue to learn about security and all other areas that would allow me to be called a generalist.&lt;/p&gt;

</description>
      <category>yearinreview</category>
      <category>2020</category>
    </item>
  </channel>
</rss>
