Thank you software developers 600x600

Software developers of the world, thank you.

software software development business

Something dawned on me the other day - something pretty amazing.

So I've been a Software Engineer, Developer - whatever you want to call it, a good chunk of my life. My dad introduced me to coding back in the early 90s, I studied Software Engineering in University, and now I run my own Custom Software shop. So software development has been a big part of my life and I would say I'm pretty proud of where I am.

However, something dawned on me the other day. I've really been standing on the shoulder of giants and what's crazy is - so many of us have.

Let me explain.

If you don't develop software, what you should understand, is that software is built in layers of abstraction. As a developer, you don't need to know how electrons zip back and forth on a computer motherboard because someone built a layer on top of that that abstracts the details away from you. As a developer, you don't need to understand how certain low-level operating system operations work because someone built a layer on top of that.

But it doesn't stop there.

Let's take Ruby on Rails for example. This is my web framework of choice. Because of Ruby on Rails, I don't need to understand when someone types in a URL in their browser, how that sends a request to my code with all the information I need to build a web application. I just use that information and I'm on my way.

But it doesn't stop there.

Many other developers have developed libraries that makes it infinitely easier to send fancy email notifications, create realtime web applications, render fancy charts and more.

And you know what, it doesn't stop there.

This is the thing - as a software developer, you leverage other people's work so much. You use frameworks, libraries, code snippets - you name it. Let's take a closer look.

As an informal, non-scientific experiment, I decided to pick one of BiteSite's biggest projects and I wanted to see how much code was ours and how much wasn't. Remember, this is pretty informal, but it should give you an idea

  • Our code (~4MB)
  • Library & framework code (~527 MB)

(for the technical readers, I took the size of our Ruby on Rails app folder for "Our code" and took the "node_modules" and "gems" folders for "Library and framework code")

That's 0.75% for our code! 99.25% of the code that's being used was written by other developers.

Now this is not super scientific, and granted we're not using every bit of the framework - but it's just a small indication of how much code we're using that we didn't create ourselves. I would also say that this is probably quite typical of most applications.

Aside from the exact percentages, which only represent the code that was written - there's so much research and development that went into creating these libraries and frameworks. Not to mention that a lot of these libraries and frameworks contain the work of some of the best engineers in the world. Think about that.

I can't count the number of times a client has come to me and said "Can you do this?", I say "Probably", Google it and within minutes have an amazing solution because some developer out there coded an amazing library.

And it doesn't stop there.

The benefit goes way beyond making a software developer's life easy or making them look impressive. Because of these libraries and frameworks, the developers using them are able to build incredible apps that are the foundation for amazing businesses and companies that change the world.

And here's the kicker. They do it for free.

That's what really blew my mind as I thought about this more and more. A lot of massive, super successful companies that change the world build their software, their company, their success using free frameworks and libraries.

We have all this amazing technology at our disposal because a huge number of software developers love developing amazing technology, putting it out there, and ask nothing in return.

Don't get me wrong. I know there is sometimes a bigger business motive for some of these libraries and frameworks, but for the most part, I think software developers have developed such an incredible culture of sharing and collaborating for the greater good. I can't think of another industry that does it so well.

I've taken it for granted for so long that so much of what I do and so much of what the tech industry does is based on (and forgive the phrase) the kindness of strangers - the kindness of developers who love their craft and want to share it with the world.

So with that - I think it's time we all thank this incredible group of people who continue to provide answers to our problems, who allow us to build amazing world-changing products and businesses, and who ask for nothing in return.

Thank you to all the incredible software developers out there who have allowed me and so many others to get to where we are.

Caseyli
Casey Li
CEO & Founder, BiteSite
Defaultblog

Ruby on Rails QuickTip: Wrapping simple chunks of code in a method to give it meaning

ruby ruby on rails software

Let's say you have a model of a User. Let's say that if the user's birth year is 1990 or before, then you can't edit that user's first name. So you might have something like this:

# User Class
class User < ApplicationRecord
end

and you might have something like this

# User controller
class UsersController < ApplicationController
  def edit
    if user.birth_year <= 1990
       # don't allow editing of first name
    end
end

However, I would argue, that even though it's a really simply check, that you should put this in its own method:

# User Class
class User < ApplicationRecord
  def first_name_locked?
    birth_year <= 1990
  end
end
# User controller
class UsersController < ApplicationController
  def edit
    if user.first_name_locked?
       # don't allow editing of first name
    end
end

Very simple implementation and achieves the same result, but here are some advantages

  • The condition is now given meaning so that developers who are using it can understand its intent
  • Any developer reading any code that uses that method will understand more easily what's happening
  • If the business logic changes for locking the first name, you'll have to edit only one piece of code

So even the simplest pieces of code can benefit from wrapping them in a method.

Caseyli
Casey Li
CEO & Founder, BiteSite
Defaultblog

Ruby on Rails QuickTip: Adding Parameters to your Methods Safely

ruby ruby on rails software

This is going to be a quick post and applies to a lot of different languages, but it's something we've been doing in our Rails projects a decent amount.

If you're ever working on a larger codebase and you decide you want to add a parameter to a method, but are afraid to do so because it might break code elsewhere, consider simply adding a default value.

Take the following setup for example

class MyModel < ApplicationRecord
  def my_method(user_id)
    user = User.find user_id
    user
  end
end

Now, let's say you want to modify this method so you can add another parameter. Here's a safe way you can do it:

class MyModel < ApplicationRecord
  def my_method(user_id, new_parameter=nil)
    user = User.find user_id
    if new_parameter
      do something
    end
    user
  end
end

Now that new parameter's default value could have been anything, but the key here:

  1. Have a default value so that any code calling this method with the original parameters will still work
  2. When the new parameter is not passed in, have the method behave the exact same way it did before

With these two principles you'll be able to extend your code without breaking any existing code.

Caseyli
Casey Li
CEO & Founder, BiteSite
Ruby on rails accepts nested attributes for deleting records

Ruby on Rails accepts_nested_attributes_for is deleting my associated record!

ruby on rails software

So I've been programming Ruby on Rails for about 9 years now and I'm still learning new things everyday. This is one that definitely caused us some issues.

Setup

So here's the setup. We have a User model

class User < ApplicationRecord
  has_one :profile, dependent: :destroy
  accepts_nested_attributes_for :profile
end

And we have an associated Profile model

class Profile < ApplicationRecord
  belongs_to :user
end

Notice that on the User model, we have

accepts_nested_attributes_for :profile

If you don't know what that does, it basically allows you to run creates and updates on the User model and Profile model in one single call. So for example, you can do this:

user = User.first
user.update({ email: 'test@example.com', profile_attributes: { first_name: 'Casey', last_name: 'Li' })

If you set up your form correctly and with proper strong params, you can put the User and Profile all in one single form for the user making it easy to update both models at the same time. BUT BE VERY CAREFUL!

Problem

If you have a very similar setup to what we had, basically a one-to-one relationship, then you have to be very careful of your update calls. If you call update on the parent record (in our case the User record), and pass in child attributes, if you DON'T pass in the ID, it will actually delete your existing child record and create a new one!. Yah. I didn't know that either. Try it out, and look at your Rails logs, you'll see a SQL delete statement followed by an insert.

Again, this is for one-to-one relationships only while doing accepts_nested_attributes_for updates.

Solution

If this is the behaviour you want, obviously you're ok. But for us, we wanted to update the existing child record rather than destroying what already existed.

If you want to update the existing record, there are two things you can do.

Solution 1: Pass in the ID of the child record

While I haven't tried this myself, if you pass in the child record's ID, it should perform an update rather than a delete/insert.

user = User.first
profile = user.profile
user.update({ email: 'test@example.com', profile_attributes: { id: profile.id, first_name: 'Casey', last_name: 'Li' })

Solution 2: Use the 'update_only' option

This is the solution we went with. When declaring your accepts_nested_attributes_for, you can pass in the update_only option:

class User < ApplicationRecord
  has_one :profile, dependent: :destroy
  accepts_nested_attributes_for :profile, update_only: true
end

Learn something everyday. Hope this helps out some peeps. Thanks for reading!

Caseyli
Casey Li
CEO & Founder, BiteSite
Carrierwave 403 forbidden fix

Fixing Rails + Carrierwave + Amazon S3 403 Forbidden Error

amazon s3 carrierwave ruby on rails coding software

So we've been using CarrierWave for a long time now for our Ruby on Rails projects. Even when we converted to direct upload, we were still using CarrierWave for image processing. It has stood the test of time and I've heard from other developers that it offers more flexibility than ActiveStorage in its current state.

The Problem

We were implementing a very basic CarrierWave solution over the past while to upload files and attach them to an active record. The requirement was that the files that were uploaded needed to be publicly visible (so default CarrierWave behaviour). It's was very standard, basic CarrierWave:

  • Create an Amazon AWS Account
  • Create an IAM User that has full access to S3
  • Create a Rails initializer to use the IAM User Keys and Fog/AWS
  • Create a CarrierWave Uploader and attach it to the ActiveRecord model
  • Add the appropriate fields to the form
  • Get the user to upload files!

All good! This is what we've been doing for years and things have been working great.

But during this implementation, we got the following error:

Excon::Error::Forbidden (Expected(200) <=> Actual(403 Forbidden)
excon.error.response
  :body          => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>(obfuscated)</RequestId><HostId>(obfuscated)</HostId></Error>"
  :cookies       => [
  ]
  :headers       => {
    "Connection"       => "close"
    "Content-Type"     => "application/xml"
    "Date"             => "Thu, 14 Mar 2019 12:34:03 GMT"
    "Server"           => "AmazonS3"
    "x-amz-id-2"       => "(obfuscated)"
    "x-amz-request-id" => "(obfuscated)"
  }
  :host          => "(obfuscated).s3.amazonaws.com"
  :local_address => "(obfuscated)"
  :local_port    => 49582
  :path          => "(obfuscated)"
  :port          => 443
  :reason_phrase => "Forbidden"
  :remote_ip     => "(obfuscated)"
  :status        => 403
  :status_line   => "HTTP/1.1 403 Forbidden\r\n"
):

What was going on?

Realization of the issue

So it turns out the issue has to do with the fact that what we needed and what Carrierwave by default is set up to do does not play nicely with Amazon's current default S3 settings. We needed our app to upload files and make them publicly visible. However, when you create a new S3 bucket - it is not by default set up to allow that.

Solution

So how to you fix it? You want to go to your bucket and click on the "Permissions" tab. From there you want to make sure that both "Block new public ACLs and uploading public objects" and "Remove public access granted through public ACLs" are set to false. There is a little "edit" link in the upper right to allow you to set this. You can even read the little blurb about these settings if you hover on the "i" icon when you go to edit and you'll see that these control, you'll see that these affect objects uploaded with public ACL settings.

So set those to false and that should resolve the issue.

Be careful

Now one thing I should mention is that you might want to be careful when it comes to your app and your specific security requirements. Our app was all about posting public files. If you're in a situation where you need more fine grained privacy control or are dealing with sensitive files, you might want to look into creating custom policies and custom users. The solution I've detailed here is a barebones, public file upload solution. It's also a good way to just get things going if you're in a prototyping phase or in that phase where you just needs to get things working.

Hopefully this helped some of you out.

Thanks for reading.

Caseyli
Casey Li
CEO & Founder, BiteSite