My Experience as a Remote Software Developer

It is said that challenges are what makes life interesting, and overcoming these challenges is what makes life meaningful. Mid last year, I took a leap into an interesting new world of remote software development. As I got my first contract letter – I signed this electronically and if felt weird – I was filled with both excitement and uncertainty. How could I possibly contribute to a team without personal/physical contact with team members? How I assure them of my work output from miles away? Will they pay me on time? What happens if they do not pay me? How do I complain? All these questions, though some were silly, bothered me.

 

With all these uncertainties, I knew it was the right step. Working in Lagos, Nigeria has been a stressful experience where I got up by 4am, left my house by 5pm to beat traffic (I worked at Lekki before), and came back at 10pm. At some point, I felt extremely indifferent working because of the stress of commute. One day, I told myself “Enough is enough”, or as my Yoruba friends will say  “Otoge”. Also, I realized that I could accomplish far more than I currently was and, without limiting myself to location, I could grasp those opportunities. So with many job applications and interviews, I finally landed my first remote gig!

 

On my first day of remote work, I realized that I did not need to take a bath, or dress up, or step outside my room. That feeling was weird and oddly satisfying. I could put on a T-shirt and my underwear and I’m ready for work? Cool!. As the first few days of onboarding passed, I had to pick a work schedule that was comfortable for me. I worked from 8am to 5pm with an hour break at noon. This schedule works perfectly for me because I can wake up, say my prayer, exercise, go for mass and start my work by 8am, and by 5pm, I can rewind, relax and get personal work done afterward. Once I got used to this schedule, everything else went smoothly. The norm of working late and overtime in my previous work-place disappeared and I always found myself wrapping up my work by 5pm regardless of whether I accomplished the tasks for the day or not – face it, things can always be done the next day after a much-deserved rest.

 

Working remotely also has its challenges. My main challenge was to maintain the discipline to work at my specified time period. For me, there is this temptation to say “the work doesn’t matter”, or “it’s not worth it”, or ” I can make up for lost time”, and most times, giving in to this temptation ruins my whole week because my work either gets negatively impacted, or creeps into my personal life and affects it. Also, I noticed that due to a lack of physical activity, I started to gain weight. Since I did not spend hours trekking, or jumping buses to get to and from work, I wasn’t burning the calories that I should have been burning. Trying to counteract this effect with exercise has been difficult though, well, because I’m a lazy human being. But still, I try!

 

Arguably one of the most frustrating parts of working remotely is that you get to interact with your teammates in an asynchronous manner. This means that there is no guarantee that you will get a reply from your request at the moment you make it. I work with developers across different time-zones and great care has to be taken to plan communication so that it does not affect your work output. There was a time I needed help fixing a bug and no one, literally no one, responded to my requests. It was a hair-pulling situation, and with time I got to understand that eventually, they would reply and I had to be patient. The virtue of patience is key. So, these days, whenever I get stuck in communication, I just move on to some other thing and then switch back when I get the reply. Another thing I am still getting used to is the over-dependence on KPIs and metrics. Although I understand this quite perfectly – there needs to be a way the company knows you are really working – I still find measuring the output of work with Jira tickets, clocking out and clocking in, and daily status reports to be cumbersome.

 

It has been a crazy ride for me, and personally, I am loving working remotely. For those thinking of making this leap, I say, life is too short, just do it!

API Error Handling in Spring Framework: Using Exception Handlers

A TopTal post on Spring Boot rest API error handling here gives a foundation on error handling with Spring Boot. This article will focus on using error codes for characterizing errors; assigning exception handlers to groups of controllers; and the order of precedence in exception handling using exception handlers.

The practice of wrapping errors in an ApiError object, as described in the aforementioned TopTal blog post helps us to remove traces of implementation details (like exception names and stack traces) from our error responses and ensure that error responses always provide value to the consuming client.

 

Utilization of Error Codes

In addition to an ApiError object, an integer representing the specific error can be added as a property of the ApiError object. This enables the creation of a faster, more comprehensive, and extensible error handling implementation for the consuming client. If an error code is not used, the consuming client will be forced to process string representations of errors in addition to processing the HTTP status code. This is more CPU intensive than a simple integer comparison when error codes are used.

Error codes also provide a way of standardizing an API. A proper documentation of error codes returned by an API is sufficient for an API client to perform error handling when consuming the API.

By decoupling the HTTP status code from the error codes using error code status resolver implementations, we provide a way to change the HTTP status code of an error in an organized way, without modifying error handling or business logic.

Below is an example of an APIError class that can be used:


@Data
@Builder
public class ApiError {
private LocalDateTime timestamp;
private String error;
private int errorCode;
@Singular
private List<ErrorCause> causes;
private ApiError(LocalDateTime timestamp, String error, int errorCode, List<ErrorCause> causes) {
this.timestamp = timestamp == null? LocalDateTime.now(): timestamp ;
this.error = error == null? "": error;
this.errorCode = errorCode;
this.causes = causes == null? Collections.emptyList(): causes;
}
}

view raw

ApiError.java

hosted with ❤ by GitHub

Error Handling

Now that we have an ApiError class, how do we properly handle errors in API’s? Most errors that occur in API methods are cross-cutting concerns and it is good practice to separate the business logic from the error handling logic. This enables the same error handling logic to be used for multiple API methods without code duplication. The Spring MVC framework caters for this separation of concerns with the concept of Exception Handlers.

An @ExceptionHandler annotation labels a method in a controller or a controller advice as an exception handler. Exception handler methods are called when an exception (root or cause) thrown by the controller matches the exception handled by the handler.

To demonstrate this, we create HelloWorldController to return a SyntaxException when the name property contains a hyphen (apparently, we don’t like hyphenated names here lol).


@RestController
public class HelloController {
@PostMapping(path = "/sayHello")
public HelloResponse sayHello(@RequestBody HelloRequest request){
if(request.getName().contains("-")){
throw new SyntaxException();
}
return HelloResponse.builder()
.message(String.format("Hello %s!!", request.getName()))
.build();
}
}

Controller Exception Handlers

To Handle this exception with a controller exception handler, we declare a method in the controller annotated with @ExceptionHandler as shown:


@RestController
public class HelloController {
@PostMapping(path = "/sayHello")
public HelloResponse sayHello(@RequestBody HelloRequest request){
if(request.getName().contains("-")){
throw new SyntaxException();
}
return HelloResponse.builder()
.message(String.format("Hello %s!!", request.getName()))
.build();
}
@ExceptionHandler(SyntaxException.class)
public ApiError handleSyntaxException(){
return ApiError.builder()
.error("Syntax Error (from controller)")
.errorCode(991)
.build();
}
}

Once we make a post request with a hyphenated name, the syntax error occurs as shown below:


{
"timestamp": "2018-09-10T20:43:56.077",
"error": "Syntax Error (from controller)",
"errorCode": 991,
"causes": []
}

ControllerAdvice Exception Handlers

Exception handler methods can also be added to a ControllerAdvice. ControllerAdvices are component beans that contain methods that perform cross-cutting operations for multiple controllers. This is the most common way of writing ControllerAdvices because it decouples the exception handling logic from the business logic by placing them in separate files. To implement this, we create a ControllerAdvice and implement the desired method just as before.


@RestControllerAdvice
public class HelloControllerAdvice {
@ExceptionHandler(Exception.class)
public ApiError handleGeneralException(){
return ApiError.builder()
.error("General Error (from controller-advice)")
.errorCode(500)
.build();
}
@ExceptionHandler(SyntaxException.class)
public ApiError handleSyntaxException(){
return ApiError.builder()
.error("Syntax Error (from controller-advice)")
.errorCode(991)
.build();
}
}

In order for this implementation to work, the exception handler in the controller needs to be removed. This is because controller exception handlers have more priority than controller advice exception handlers.

Here, @RestControllerAdvice was used instead of @ControllerAdvice. The latter is a composite annotation of @ControllerAdvice and @ResponseBody

Applying ControllerAdvices to Selected Controllers

ControllerAdvices can be limited to “advice” specific controllers. This group can be defined by base package name, superclass, or annotation. Any of these fields can be set in the properties of the @ControllerAdvice.

Note that since exception handlers are applied at runtime, having selectors such as this reduces performance since it gives the application one extra condition to check in order to decide whether or not to apply the handler.

Matching Exceptions to Handlers

At runtime, the spring MVC framework selects one exception handler to handle an exception thrown by a controller. The selection algorithm follows these rules:

  1. Exception handlers declared inside the throwing controller are selected in preference to exceptions declared in ControllerAdvices.
  2. Among methods of an advice bean, a root exception match is preferred to a cause exception match.
  3. An exception match (whether root or cause) in an advise bean with a higher priority, specified by (@Order annotation or Ordered interface), is selected in preference with those of lower priorities.
  4. Two exception handler methods with the same target exception cannot be declared on the same ControllerAdvice. This will result in an IllegalStateException due to the ambiguous exception handler method mapping.
  5. Exception handlers belonging to the same advice are selected according to their specificity. An exact class match is preferred, then a sub-class match and so on.

Customizing HTTP Status Codes

The previous examples return an error response with the HTTP INTERNAL SERVER ERROR (500) by default. To change this, we can either return a ResponseEntity object or use the @ResponseStatus annotation.


@RestControllerAdvice
public class HelloControllerAdvice {
@ExceptionHandler(Exception.class)
public ResponseEntity<ApiError> handleGeneralException(){
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(
ApiError.builder()
.error("Syntax Error (from controller-advice)")
.errorCode(500)
.build()
);
}
@ExceptionHandler(SyntaxException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ApiError handleSyntaxException(){
return ApiError.builder()
.error("Syntax Error (from controller-advice)")
.errorCode(991)
.build();
}

Summary

Exception handlers provide a convenient way to handle API errors. API Error Objects make error description more user-friendly and error codes help to efficiently characterize errors.

Codebase

The codebase for this tutorial can be found here.

Keep Koding Konstantly.

Adventures with Hyperledger Sawtooth Core

From the first paragraph of the introduction to hyperldeger sawtooth documentation: 

Hyperledger Sawtooth is an enterprise blockchain platform for building distributed ledger applications and networks. The design philosophy targets keeping ledgers distributed and making smart contracts safe, particularly for enterprise use.

It’s an ambitious open source distributed ledger framework that aims at providing a high rate of transactions per second (it claims a thousand per second); configurable consensus mechanisms; transaction processors (smart contracts) written in any language; and an overall modular architecture.

We took a keen interest on this platform because of its immense capabilities, so, in order to find out if this distributed ledger framework is really what it boasts to be on paper, I took the hyperledger sawtooth through a series of tests. And of course, I’m reporting my findings here.

 

About this Adventure

  • The released version 1.0 is used for all tests documented here.
  • The java sdk will be extensively used to make transaction processors.
  • All tests were done on a windows 10 system (docker toolbox)

 

Installation Requirements

Please proceed to the documentation for more information on the requirements for installation. From experience, you just need a system with enough ram and disk space to handle execution of docker containers. Docker is used for packaging the framework.

 

Installing Hyperledger Sawtooth

The installation process is simple, all that’s needed is to pull the git repository  and checkout to the version you want. In this case, we will be checking out to v1.0.0.

Cloning the Repository

git clone https://github.com/hyperledger/sawtooth-core

Checking out to v1.0.0

 git checkout tags/v1.0.0 -b sawtoothv1.0.0

Good! We now have our copy of v1.0.0 on the sawtoothv1.0.0 branch

 

Running Hyperledger sawtooth core

The code contains docker-compose files for different configurations that you could use as a starting point. Here, we will start with docker/compose/sawtooth-default.yaml.

cd /docker/compose
docker-compose -f sawtooth-default.yaml up

 

A brief introduction to the sawtooth architecture

An indepth exposee on the sawtooth architecture can be easily found in the documentation. We will just go through the basic components.

The hyperledger sawtooth system is made up of the following:

  1. Validator – This is the principal component of the sawtooth platform. It is responsible for block validation, block chaining and communicating with other validators on the network. The validator also validates transactions by delegating it to its transaction processor. It also ensures that transactions and batches are signed properly.
  2. Transaction Families – These are group transaction types that perform related actions. A transaction processor processes transactions belonging to a transaction family. Clients create transactions from a family and send it to the validator through the rest-api. The transaction processor is an independent service that communicates with the validator through TCP/UDP socket connections. A validator can be connected to any amount of transaction processors in order to process transactions belonging to different transaction families. The validator registers these transaction processors as soon as they are connected and deregisters them once they are disconnected. This way, a validator knows what transaction processor is attached to it.
  3. Rest Service – This exposes rest api endpoints for clients to integrate with the validator. A proper documentation of
  4. its endpoints be found on the sawtooth documentation. With this, we can send transaction in batches, query states, transactions and blocks, and listen for state changes through a websocket api endpoint.

 

On the sawtooth-default.yaml

This is a basic one-validator sawtooth system meant to show case the capabilities of the hyperledger sawtooth. It comprises of the following:

  1. Settings-tp: A transaction processor that processes transactions that can be used to configure the blockchain.
  2. Intkey-tp-python: An integer-key transaction processor, that provides transactions that can be used for testing.
  3.  Xo-tp-python: A tic-tac to transaction processor. Yes, you can play tic-tac to by sending transactions to the blockchain LOL.
  4. Validator – The validator (as the name implies)
  5. Rest api – The rest api (as the name implies)
  6. Shell – A command line utility for posting transactions of the various transaction families. It integrates with the rest api.

With a good internet connection, all modules are pulled quickly as docker-compose is run and you can see the colourful output on your command line.

 

Experimenting with the Integer Key

Run the following command in the docker terminal to enter into the shell environment

docker exec -it sawtooth-shell-default bash

We are going to create a batch and send to the int-key-transaction processor.

Intkey create_batch
Intkey load

 

More about IntKey

Intkey provides transactions that can be used to test deployed sawtooth distributed ledgers. These transactions are as follows:

intkey set: It sets a key to a specified value

intkey inc: this increases the value of a key by 1

intkey dec: this decreases the value

intkey show: this displays the value of a specific key

intkey list: this lists the values of all the keys.

intkey create_batch: creates a batch of intkey transactions

intkey load: Sends the created batch to the validator.

 

More about the intkey that the documentation won’t tell you

A close inspection of the intkey cli code written in python reveals how create_batch can be used. You can supply the following arguments:

-o –output:  Location of the output file (batches.inkey by default)

-c –count:  Number of batches modifying random keys (1 by default)

-B –max-batch-size:  Max transactions per batch (10 by default)

-K  –key-count:  Number of keys to set initially ( 1 by default)

 

create_batch generates a random word list (keys to set initially specified by -K or –key-count). It then generates “intkey set” transactions that initialize the values of those keys to a random number between 9000 and 100000, appending it to a batch. It generates one set transaction for each key. After this, it creates a random number of batches modifying the random keys. The number of batches created is specified by the -c / –count args.

Each batch created will have a random batch size of the range between 1 and the args -B / –max-batch-size. These transactions modify a random choice of keys and randomly increment or decrement the values.

Therefore, with the create_batch, the smallest number of transactions we can have is two batches, 1 transaction per batch. One batch for set, while another for increment or decrements.

Investigating Transaction Flow with Int Key

In order to properly investigate transaction flow, lets cover the theoretical background.
From the sawtooth documentation, the main component of the validator is the Journal, responsible for “maintaining and extending the BlockChain” for the validator. These responsibilities include the following:

  • Validating candidate blocks
  • Evaluating valid blocks to determine if they are the correct chain head
  • Generating new blocks to extend the chain.

 

A batch is a group of transactions that are atomically lumped together in a manner that when one fail, all fails. Batches can be made up of transactions from the same or different families.

The journal consumes blocks and batches that arrive at the validator via the interconnect, either through the gossip protocol or the rest api. The journal routes them internally.

The flow is as follows:

  1.  Blocks and batches arrive from the interconnect to the completer
  2. The completer ensures that all dependencies of the blocks and(or) batches have been satisfied and delivered downstream
  3. The completer delivers the blocks and batches to different pipelines.
  4. Completed Batches go to the BlockPublisher for validation and inclusion into a block
  5. Completed blocks are delivered to the chain controller for validation and fork resolution.
  6. Blocks are considered formally complete by the completer once all of their predecessors have been delivered to the ChainController and their batches field contains all the batches specified in the BlockHeader’s batch_ids list. The batches field is expected to be in the same order as the batch ids.
  7. Batches are considered complete once all of its independent transactions exist in the current chain or have been delivered to the BlockPublisher.

 

The Chain Controller

This is responsible for determining which chain the validator is currently on and coordinating any change-of-chain activities that need to happen.
The Chain Controller is designed to handle multiple block validation activities simultaneously. This means that once forks form, the Chain Controller can process these forks simultaneously. The current chain can be used while a deep fork is evaluated. Currently the thread pool is set to 1 (its hardcoded), so only one block is validated at a time.

 

Flow for updating blocks in the ChainController

  • Block enters the chain controller
  • Chain controller creates a block validator with the candidate block and current chain head and dispatches it to a thread pool for execution. Once the block validator has completed, it will callback the chain controller indicating whether the block should be used as the chain head. This call back occurs in three situations:
    • If the call back occurs after the chain head has been updated, a new block validator is created and dispatched to redo the fork resolution.
    • If the call back occurs when the chain head has not been updated, that new block becomes the chain head.
    • If the call back determines that the block should not become the chain head, then it is discarded. This occurs if there is an invalid block in the block’s chain, or is a member of a shorter or less desirable fork as determined by consensus.
  • The chain controller synchronizes chain head updates such that only one block validator result can be processed at a time.
  • The chain controller performs a chain head update using the block store, providing it with a list of commit blocks that are in the new fork and a list of decommit blocks that are in the block store that must be removed.
  • After the block store is updated, the block publisher is notified about the new chain head.

 

Block Validation Rules

In hyperledger sawtooth, block validation includes the following:

  • Transaction permissioning – Ensuring that the right entity submits transactions and batches.
  • On-chain block validation rules
  • Batch validation
  • Consensus verification
  • State hash check.

 

Block Publisher

The block publisher creates candidate blocks to extend the current chain. It does all the house-keeping work around creating a block but waits for the consensus-algorithm’s instruction on when to create a block and when to publish a block.
The flow in a block publisher is as follows:

  1.  Add new batches to scheduler
  2. Wait for consensus algorithm to create candidate block
  3. If candidate block is created, keep adding batches
  4. Wait for consensus algorithm to publish
  5. If time to publish, finalize candidate block and publish block
  6. When a block is created, an instance of Consensus.BlockPublisher is created that is responsible for guiding the creation of this candidate block. A transaction scheduler is created and all of the pending batches are submitted to it.

 

Results of intkey Testing

This will be posted soon enough, happy reading!

Life as a Software Developer

Are you new to writing code? Or have you mastered the arts and sciences of programming? Do you love coding in high level languages like python, java or C#, or you are a wizard in embeddable programming in C, or assembly language? Are you into abstract applications of programming like solving complex differential equations or do you fancy developing web applications? Regardless of what you use the skill of programming to do? We always go through the same things:

 

We have to cope with constant change

Well, to be fair, every human being (perhaps animals and plants thanks to climate) has to cope with change. But really, change in the software development world is different. We software developers have to cope with learning new processors; more exciting frameworks; more efficient programming languages; not to talk about upgrading our operating systems to support the kind of applications we want to install. As a spring-boot developer, change is common place. By the time the application you are developing goes live, the spring framework version you are using gets obsolete and the next thing that you must think of is how to upgrade. You will probably face coping with new software framework releases dealing with open source projects. It’s an awesome thing to know that frameworks are developing at a faster pace, but discipline is needed to cope with the change.

Be careful not to lose yourself

Have you ever walked along the road and someone angrily taps you on the head and yells, “Hey! Can’t you hear me greeting you.” Of course you cannot. You are busy thinking about whether or not to use reactive streams to handle event’s coming from state delta changes. But then again, you remember that the person is not in your head so you just have to apologize. On some days, the only thing I think about is solving the problem at hand. I guess the sage software developer portrayed wisdom by saying “One of the hallmarks of a good software developer is knowing when to walk away”. Indeed this is true. Many times that I resigned to the excruciating fate of not solving a problem on a particular day, after a much needed rest, I find the solution to that problem within the first hours of the next day. So once again devs… WALK AWAY.

We love all those HIGH’s

Oh, and those euphoric feelings you get when you pull of something complex and make it look simple? Who could get over that? I think it’s one of the perks plying a trade that has to do with “creating something out of nothing”. It makes you feel like you are working along-side God in doing his fantastic work of creation. One of the reasons I became endeared to programming is that with the right skill set and CPU, you could tell the computer to do anything. However, these feelings have caused a lot of developers to re-invent the wheel. Is it that you do not know that there is already a library that can handle conversion of a byte to a hexadecimal string? I mean why on earth would you want to re-create that? Although we all want to have that feeling that we created something, let’s not forget that what we create, we should be ready to manage. And if we are not prepared to manage such libraries, its best we find existing ones – ones that are managed by other organizations, there are lots of them I assure you!

 

Don’t forget those depressing moments

I recently hit a wall in development trying to figure out Hyperledger Sawtooth, and apply it to one of my projects. At that gigantic wall, faced with an abysmal feeling of despair, I had to re-evaluate my life’s choices. I also had this exact same feeling when I was trying to figure out how to implement the card personalization standard for Mastercard. With a bit of courage, I picked myself up, dusted myself and I am moving forward. Things are going to get though, like a wise entrepreneur always says, don’t let short term set-backs get to you. Think always of the long term. With a little perseverance, we will always see something really beautiful coming out from that supposedly gleam situation.

You want to know everything but then again… you cant.

I told myself one day that I wanted to explore Java 9. Out came spring framework 5, and oh, that one too! Let’s add it to my list. What about distributed ledgers? Machine learning? Big data? Artificial Neural Networks? Then the list of things to learn compiles. Don’t forget that we are on a tight deadline to deliver a feature and if you are in a fast growing organization like mine, you will be saddled with a lot of responsibilities here and there. With all these responsibilities, our learning list slowly becomes just a list and nothing more. Even worse, the list becomes a depressing agent for you because it constantly reminds you of all you failed to do. In these situations my dear, take life one step at a time. All that matters is your focus, not the amount of strides you take. Even if it takes you 10 years to finish your list (if you can even finish your list), the most important thing is that you learn something new every day. Make that a habit.

You find yourself googling or stack-overflow-ing the same thing over and over again

Well, what can I say, there’s no fix for this. Sorry.

Journey to Knowledge

Is school the only place to acquire knowledge? Should you wait to be thought in order to learn something? Are there things you should not or cannot learn?

 

At some point in time, we ask ourselves this same question. After graduation, I enrolled in a software programming course NIIT, and after completion of that course, I found out that I was basically teaching myself. I mean, the lecturer tried, to the best of his ability, but at the end, 90 percent of the work was me. When I look back at my life in the university, I ask myself if I could accomplish all I did on my own. Sure it would have taken a lot of discipline, but I think I would.

 

Life is all about discipline, motivation and sacrifice. In order to do great things, I think we should be prepared to go the extra mile independently. We should not wait for someone to push us no matter how young or old we are. And you know the most interesting thing about learning? It’s like a high that you cannot come back from. It gives you this feeling of freedom and power. I could not put it better than Sir Francis Bacon:

“knowledge itself is power”

In my first chapter of my journey to knowledge, I explore the possibilities of becoming an adept computer scientist all on your own. I found this open source course scheme of work that on Github attributed to the Open Source Society.  In my other posts, I will be describing my experiences, concept gained and giving a few tips on how to study independently.

 

I’m already getting excited!! I’ll keep you posted.

The Big Picture

“To build my own empire… “ a phrase I just learnt recently. Everyone has what he or she is passionate about, whether it is cooking, just writing, reading, playing FIFA 17 (oh yeah, that’s me) and so on. But a few people make an impact in doing what they love. I spend a lot of time working and convincing myself that I am making an impact, a difference, or at least that if I leave this earth, someone (or something) would feel it.

 

But sadly, that has not been the case, although I am part of a team that is pushing Nigeria to the next level financially by creating solutions that would eventually disrupt the fintech industry, I feel that at the end, it’s not enough. I am constantly reminded of the words of Jesus when he said:

“I have brought you glory on earth by completing the work you gave me to do.”

With this, a question keeps ringing in my head:  “Can I say that of myself when I die”?

 

To me, making an impact is multi-faceted. It has to do with how many lives I have touched, and how many communities have benefited from my presence. As a practicing catholic who is also a tech lover and passionate programmer, my domain of focus is divided roughly into my life as a growing catholic, my career as a software developer, and of course my learning adventures because I love to learn.

 

As a catholic, God has entrusted me with many responsibilities that I must live up to, my work as a servant of Mary, in the Legion of Mary, as a youth and member of the board of lectors. As a software developer, and a person who dreams of being a great mentor, I build applications for the company I work with, I try to mentor young developers and I work on small side projects. And lastly, to keep myself alive I have to read to learn, and learn to read.

 

Why I’m I saying all these? Starting a blog, ranting about boring stuff? Well, I know people out there that say that it is difficult making a difference in all aspects of your life. A lot of people tell me that “you have to drop one”. Sometimes, I even think that same way. I’m hoping this blog will inspire you that is reading to go out of your comfort zone and make impact. Not just in your career or social life or religion but everywhere… and I mean it. At the end really, that is what building an empire is all about.

Blog at WordPress.com.

Up ↑