The obstacles of our first app as interns

Mihai Ionescu
2Performant Tech
Published in
4 min readAug 10, 2018

--

Good afternoon! I am one of the 2Performant interns and in this article I will share how we solved some of the technical difficulties we encountered during our first task at the company. This is a continuation to our first blog post from this series.

While developing our game we had different phases during which our application significantly evolved and improved. In the diagrams below you could see how the connections between classes changed over time, from the first implementation until the last change. Note that this was our first project done in Ruby.

Before
After (specific color = same folder)

“Spot the differences” is an awesome mini-game but I can give you some hints about the diagrams above, different than the obvious one that our app transformed into a rainbow. Classes became more optimized on what they were supposed to do, decreasing a lot of the dependencies that were not needed in the first place.

Our code was better and cleaner, trust me :). Why? How? More insight below.

Setting standards

After a few days of working at the project we realized that reading and trying to understand someone else’s code might be more difficult than we thought, especially when everyone writes it in their own way. Respecting the standard style followed by the majority of the Ruby community helped a lot in this regard. Based on that model we chose our own code conventions.

Code repetition

DRY is a concept that sounds extremely simple, however, respecting it in a project on which multiple people work, turned out to be difficult. Well, using popular patterns helps a lot here :). If you are new to programming I highly recommend reading about design patterns because they might make your life a lot easier.

Here is a Factory that “doubled the production’’ :):

class RoomFactory
TYPES = {
room: Room,
shop: Shop,
vault: Vault,
hospital: Hospital,
monsterroom: MonsterRoom,
winroom: WinRoom
}.freeze
def self.create(type, *arg)
TYPES[type].new(arg)
end
def self.describe(type)
TYPES[type].get_room_description
end
end

Splitting our app in different folders

Once our application became larger we felt that placing everything in the same folder was not working anymore, the project looked extremely unorganized and the productivity decreased over time. We tried then to find a solution so when we change the path of a file we don’t need to modify the “requires” from the entire project. One of the basic concepts we understood was that ideally when you make a change you would like to modify only a small portion of the code from one place.

In this regard we created a configuration file that was enabling using a resource from the folders specified without calling it’s full path:

require_file.rbmain_folder = File.dirname(__FILE__)
$LOAD_PATH << main_folder
folders = %w[room test character io item map utilities menu combat]
folders.each do |folder|
$LOAD_PATH.unshift File.expand_path(File.join(main_folder, folder))
end

Cleaning the mess

Something that bothered me at the beginning that we changed was the situation in which a function with a high number of parameters had to be called. A method like this usually does multiple things and that is a form of code smell + whenever you had to call that function, having to memorize the order in which the parameters were used wasn’t really a cool experience.

The easiest solution to this problem was to insert the parameters that have similarities into new data structures.

So code like this:

def initialize(attack = 0, defence = 0, value = 0, name = ’Chest Plate’)

Turned into ❤:

def initialize(stats = Hash.new(0), name = ’Chest Plate’)      or 
def initialize(stats = Stats.new, name = ’Chest Plate’)

Maintainability

Another problem our application had was that the flow of the game sometimes was built inside classes that were supposed to serve as models. This made the code unintuitive and hard to modify whenever, let’s say, a typo showed up on the game screen.

To solve this problem we tried to separate our application in multiple layers, based on how close or far something was from interacting directly with the user. We found that separating the code in this way made it easier to test it as well. This is probably one of the hardest tasks to do for someone inexperienced and our project could probably still be improved a lot in this regard.

We built for example a class called “IOTerminal” whose role was only to print the strings that were sent by the lower layers to the terminal.

Tests

Initially manual testing looked awesome, however, we realized that it was not the best solution on the long run, the bigger our application was getting, the more difficult and time consuming it was to check if everything is working as intended. This is why we implemented automated tests — and the difference was bigger than I expected, the productivity improved a lot.

Conclusions

I hope this gave you a good idea about the difficulties you might encounter while working for the first time in Ruby.

In the next episode we will discuss more about the evolution of our project so stay tuned :).

Have a nice day and see you around *insert link to the next article after it is published* :).

--

--