Showing posts with label best practices. Show all posts
Showing posts with label best practices. Show all posts

Friday, December 5, 2014

A Story about YAGNI - You-Aren't-Going-To-Need-It

Recently I was working for a project on a client where they wanted the project to be Live for a certain portion of time during the day - so for example between 9AM and 2PM, or 12PM and 3PM, or so on and so forth.

While implementing this feature, a thought struck me - what if the client wanted to have a period that ran past midnight - so for example between 11PM and 2AM? At this point, the intelligent thing to do would have been to pick up the phone and call the client to ask. Instead, I thought to myself - well, even if they haven't asked for it now, they might need it later. Besides, I could build a decent set of unit tests and use them to catch all the edge cases, right?

Wrong.

What ended up happening was that I spent many hours writing the unit tests to capture the many edge cases that came with crossing the midnight deadline during a time period. I spent even more time writing the code to deal with them. The project went over-budget thanks to that extra time I spent, and the client never ended up using the added feature. After a few months in production, the client scrapped the project (albeit for unrelated reasons). 

Lessons learned: unless you're very strongly confident that you need to build something for the future, don't do it. Don't fall prey to what-if's. Remember: YAGNI - you aren't going to need it. And if you do need it, you can build it later, when it's accounted for in the budget. Finally, don't be afraid to pick up the phone and ask a question. It makes everyone's life easier.

Sunday, November 16, 2014

Learning Angular | Using a factory to make an API call

I was recently building an Angular app in which I had a search page and a search results page. Obviously, the API call would be made after a user hit the search button on the search page, but the question was whether I should make the call from the search page or the search results page.

Search page on the left, search results on the right
My initial solution was extremely complex - I tried recording the selected parameters, and then I created a super-complicated route that allowed me to pass them into the the search results page. However, that was way too complicated, and I quickly realized that there had to be a better way.

After asking around a bit on some Angular help channels, I figured out that Angular services and factories are singletons! This means that I can make the call in the search controller, store the results in my API factory, and then bootstrap my search results controller user the cached results. This ended up being a much faster, simpler, neater implementation!

Here's how my final implementation worked:

Search function - searchController.js

$scope.search = function() {
        var searchParameters = {
            // create search object
        }
        
        API.search(searchParameters).success(function (data, status, headers, config) {
            $location.url('/results');
        });
    };


Controller - searchResultsController.js

// gets the cached results from the API
var data = API.getSearchResults();

    

API factory - API.js

angular.module("donorsApp").factory("API", ["$http", function ($http) {
    
    var constructSearchUrl = function (searchParameters) {
        //returns URL;
    }
    
    // cached results
    var results = null;
    
    return {

        // gets called from the searchController             
        search : function (searchParameters) {
            var request = $http.jsonp(constructSearchUrl(searchParameters));
            
            request.success(function (data, status, headers, config) {
                // caches the returned results
                results = data;
            });
            
            return request;
        },
        
        // gets called from the searchResultsController
        getSearchResults: function() {
            return results;
        }
        
    }
    

}]);

Lesson learned: don't make API calls from the controller, make them from a factory or a service, and then cache the results as needed. 

Monday, February 10, 2014

Thoughts on the Single Responsibility Principle



Today, while reading 97 things every programmer should know (disclaimer: affiliate link), I came across an explanation of the Single Responsibility Principle that finally clicked for me. I've read a fair amount of literature on Best Practices for programming, but I've often faced trouble in applying these best practices in real life projects. Here was an explanation that finally made sense.


The Single Responsibility Principle says that each class should have only one reason to change. This makes perfect sense in theory, but somehow I just couldn't find a way to implement it in practical code. However, after reading this chapter in the above book, I realized that I was getting the semantics wrong - objects in OOP don't have to correspond to objects in the real world. You don't need to have a single (possibly quite large) class for an Employee that handles the business logic related to Employees (eg. calculating wages, sick days, etc), as well as the database logic (inserting into and retrieving from a database), as well as any report generation logic - classes should be more flexible and correspond to what makes them easier to maintain. 

The chapter linked above, for example, says that instead of having an Employee class with functions that handle business/database/reporting logic, it makes more sense to have an Employee class that handles the business logic, then an EmployeeRepository class for the database logic and an EmployeeReporting class for the reporting logic. This means that if the database logic ever needs to change, I will only be touching the EmployeeRepository class that handles only the database logic. The business and reporting logic will remain untouched in their respective classes. Now, each class truly has only one reason to change.

Conclusion: don't be rigid in insisting on having classes map to real-world entities. Code your classes based on what makes them easiest to read and maintain.