What I've learned since graduating from a coding bootcamp, Part 1

A slight departure from the usual programming but necessary.

Since graduating from Flatiron School in late October 2020, I have gone through different refactors of my weekly schedule, bought various courses on Udemy, and tried to research the best, free options to expanding on my technical education. So I felt that I should write this kind of a post to tell you what I learned about productivity and the new strategy that I’m doing now to help someone in similar shoes.

I wanted to make this blog post interactive with opportunities for you, the reader, to answer personal questions for yourself to create your own productivity strategy.

It’s the journey, not the destination

Avoid the Burnout

I remember the Monday after graduating and attempting to start learning the same way I did every day for the previous three and a half months.

💡Tip #1 — Give yourself sometime to recharge. It’s not a race!

I didn’t realize it but the program had burnt me out tremendously and then I took the next week off completely.

Yes, you need to plan a schedule, but it needs to be a schedule that you can commit to. You have to be honest with yourself and know your own work ethic.

Personally, I’m the type of person who can’t sleep well if I have outstanding questions and research that needs to be done. Therefore, I now space out my days more so that if I come across something that peaks that side of me, I can spend 4 hours or more researching the topic and learning why it works the way it does without the guilt of not completing everything on my to-do list.

Question #1 — Being honest with yourself — What is your work ethic? How long can you stay focused?

Creating Your Schedule

Based on your answer for the previous section’s question, this section will be short and you can answer the question easily.

Creating your schedule depends on how you learn, your work ethic, and your ability to stay focused and distraction-free during the day.

💡Tip #2 — Always schedule in breaks.

After graduating from the bootcamp and after some refactors since then, my strategy was centered around learning a little bit each day. I would start my morning with breakfast and watching a Udemy course on data structures and algorithms. (Yeah, I hate them, you hate them, and loads of Senior Devs hate them … but for some reason, it’s still a common practice to hire via whiteboarding problems.) The early afternoon consisted of lunch and learning Redux (or at least it was at the time / learn any topic that peak your interest) for two and half hours. Late afternoon to about early evening (around 3:30 to 6 pm), I was learning another topic or doing research or taking another video course, like the UX design one I did in the winter. The evening was pretty easy, dinner and work on a project.

At a glance, this schedule might seem nice and well-balanced and it was, until I hit road blocks in your learning where things take extra time. For example, let’s say you’re doing algorithm practice and can’t solve a hard problem, do you give up and comeback tomorrow or do you want to struggle more and attempt to solve it the same day?

There’s no right or wrong answer. I would hit similar roadblocks and struggle so my schedule would be backed up and I would feel guilty about being behind on my progress or lack thereof.

These days I have balanced out my schedule more. I go for a nickel-and-dime approach and doing a little bit each day. My daily to-do list looks something like this:

  • Network and contact at least two people
  • Try to apply to two jobs
  • Work on a project piece-by-piece throughout the day
  • Weekly - Learn something new
  • Weekly - Learn a data structure or algorithm (this is new because I just started learning these again.)

My day is structured and spaced out. I don’t feel overwhelmed and I can see my progress my focusing on new topics each week and practicing with them by building them into projects.

Question #2 — How do you learn?

…Now, the technical part

I thought that I should add a section of areas where I’ve improved upon and new things I learned via my new productivity strategy as well as general technical concepts that I think bootcamp grads should think about.

In this post, I’ll talk about adding more security to authentication when using an

Secure Authentication using JWT (JSON Web Tokens)

I’m not sure if this is something taught in all bootcamps but JWTs (JSON Web Tokens) can be incorporated in other programming languages, especially if you’re creating an SPA (Single Page Application) with a hosted backend. In my case, I’ll demonstrating with Rails API-only backend and a React frontend using Redux for state management (don’t worry, a proper Redux blog series is in the works).

In Flatiron, they taught us to use JWT in our authentication for our applications after we moved away from using Rails to build a full-stack application. We were taught to store our tokens via localStorage.

localStorage

All you need to know localStorage is that it works similar to a cookie where you store data in a key-value pair. However, this object is easily accessible with JavaScript, which doesn’t provide the necessary security you would need in a real-world application. When searching online for localStorage security you’ll find debates and how JWTs should be stored and the different attacks you expect from either option, CSRF and XSS attacks.

Storing your token in localStorage will make vulnerable to XSS attacks. Similarly, if you store your token inside an HTTP-only cookie, you can be vulnerable to CSRF attacks. However, you would need to restrict requests to only specific domains, here’s a sample of what should look like:

Rails.application.config.middleware.insert_before 0, Rack::Cors do
        allow do
        origins 'localhost:3001'
#! Restricted requests to only come from localhost:3001
#! Once I host my project, I will replace this with the hosted domain name

        resource '*',
        headers: :any,
        methods: [:get, :post, :put, :patch, :delete, :options, :head],
        credentials: true
    end
end

To protect against attacks, I opted to store my token inside of an HTTP-only cookie so that it’s only accessible from the server-side. When a user logs in on the frontend, the data is sent to the server via a POST HTTP request where the user is found by email and then we match the password that the user inputted on the form. If both the email and password are correct, we create a token by encoding their id. Then, we create a signed cookie using the encoded token that we created.

cookies.signed[:jwt] = {value: token, httponly: true,   expires: 1.hour.from_now}
#! Here I have the cookie expire every hour

auth_controller.rb

class Api::V1::AuthController < ApplicationController
    skip_before_action :authorized, only: [:create]

		def create
			@user = User.find_by(email: user_login_params[:email])
			if @user && @user.authenticate(user_login_params[:password])
				token = encode_token({ user_id: @user.id })
        cookies.signed[:jwt] = {value: token, httponly: true,   expires: 1.hour.from_now}
        render json: { user: UserSerializer.new(@user), jwt: token }, status: :accepted
			else
	      render json: { message: 'Invalid username or password' }, status: :unauthorized
			end
    end

    private

    def user_login_params
        params.require(:user).permit(:email, :password)
    end
end

For the next part to make sense, you should look at the this code snippet:

application_controller.rb

class ApplicationController < ActionController::API
    include ::ActionController::Cookies
    before_action :authorized

    def encode_token(payload)
#user logins or renews their sessions via the client
#The payload is the user_id that we are passing through
        JWT.encode(payload, 'random_secret_key', "HS256")
#The JWT.encode method takes in multiple arguments
# # we're taking the payload, using a string key of your choice, and the algorithm we want to use
# # # # The algorithm parameter is usually optional however we need it because we're going to be decoding the token with the same algorithm
    end

    def token_from_cookie
# Remember in the previous snippet that we created a signed HTTP-only cookie
# As I mentioned, it can accessed only via the server
# this method simply return the token
        cookies.signed[:jwt]
    end

    def decoded_token
        if token_from_cookie
#if we have a HTTP-only token coming from the client
            token = token_from_cookie
# assign it to the variable token
            begin
                JWT.decode( token, 'random_secret_key', true, algorithm: 'HS256')
#once that condition is true, we begin to decode it
#We decode the token similar to how we encoded it
# # Our parameters -> the token, our key, we're verifying true, and the algorithm
            rescue JWT::DecodeError
                nil
            end
        end
    end

    def current_user
        if decoded_token
# If we were able to decode the token
            user_id = decoded_token[0]['user_id']
# the return value is an array, we want the first index ([0]) and grab the user_id number (i.e. 1)
            @user = User.find_by(id: user_id)
# We then return a user object after we found that user via the id number
        end
    end

    def logged_in?
        !!current_user
    end

    def authorized
        render json: {message: "Please log in"}, status: :unauthorized unless logged_in?
    end
end

There are two methods that I want to show you that deal with a user logging in and staying logged in on the client. When our user logs in and it’s a 200 code, we send a GET request to the API which runs the profile method below so that we can return the user object.

As you can see it calls on current_user. In the code snippet above, current_user is a method in the ApplicationController. This method calls on the decoded_token method as well.

users_controller.rb

class Api::V1::UsersController < ApplicationController
    skip_before_action :authorized, only: [:create, :session_renew]
...

	def profile # Returns a user object
		render json: { user: UserSerializer.new(current_user) }, status: :accepted
	end

	def session_renew # Returns a user object && renews the cookie and token
		user_id = current_user[:id]
# If we previously logged in that means we have the HTTP-only cookie on our browser
# We go through all of steps mentioned in the comment for decoding and returning the user
#Assign the id number to the variable user_id
		token = encode_token({ user_id: user_id })
# We encode the id and create a new token
		cookies.signed[:jwt] = {value: token, httponly: true,   expires: 1.hour.from_now}
#Create a new signed HTTP-only cookie which also updates the expireation date
		render json: { user: UserSerializer.new(current_user) }, status: :accepted
#Returns a user object
	end

...
end

When we’re on the client side, we create an object in localStorage, something simple with no defining information. You create a object like { isLoggedIn: true } and in the client, if isLoggedIn === true, we can run a function that will send a GET request to the server and invoke the session_renew method. (We use a GET request because it’s a much safer option than a POST request which could get hijacked.) Additionally, the session_renew method, where we create a new token and renew the cookie token and expiration date, is invoked each time we reload a page or visit another part of the site.

Conclusion

I decided to break this up into separate parts so that you can first work on a schedule the strategy that works for you.

Next week’s post will be about the job search and something I used for the first time, styled-components in React!