XNSIO
  About   Slides   Home  

 
Managed Chaos
Naresh Jain's Random Thoughts on Software Development and Adventure Sports
     
`
 
RSS Feed
Recent Thoughts
Tags
Recent Comments

Archive for the ‘Agile’ Category

World’s First Agile Job Fair at Agile India 2014 Conference

Saturday, January 11th, 2014

Come, join the very FIRST Agile Job Fair in the World!

A platform dedicated for the Agile practitioners to meet their potential Agile employers.

Agile India Job Fair is being organised by Agile Software Community of India, a registered non-profit society. We have been running conference and other events in India since 2004. This job fair is on the very next day, after our international conference – Agile India 2014, which attracts about 1000 international participants.

Why a job fair?

Agile methods have become mainstream and they are here to stay. In India, many companies are having a hard time finding needles in the haystack .i.e. finding really good Agile practitioners from a whole lot of posers.

The few, really good practitioners out there, have a similar problem. Every company wants to hire Agile people, but are they ready? Do they really believe in Agile culture and even have an agile mindset?

Many practitioners want to talk to real people from the company to really understand the culture of the organisation and the nature of the work.

Browsing the classifieds or surfing the Internet or talking to headhunters (recruiting companies) can only get you so far.

To solve this problem, we are creating a first-of-its-kind, unique opportunity where job-seekers can meet several top Agile employers face-to-face under one roof, clarify their doubts, interview with potential companies and also socialise with other candidates.

Walk-In to explore a gamut of Agile career opportunities with the best Agile employers in India.

What kind of candidates would this event attract?

We have a database of 56,512 software professionals from top companies in India. We will market this event to all these folks. However, who will attend will largely depend on the kind of companies that will be participating to hire candidates. We would filter the companies, to make sure only top companies are part of this event and hence ensure that we would be able to attract really good practitioners.

What can a participating Company (Employer) do to attract participants?

Seeing is believing! So we would strongly suggest you give participants a glimpse of your work culture. May be setup a pair-programming station and project the programming session on a large screen. May be you can setup a story card wall. Showcase some the nature of problems your company is solving. Run a slideshow of pictures from your office. And may more. Just get creative!

What is the cost to participate?

This is a non-profit event. There are 2 major costs, the hall rental and the cost of setting up stalls. We would pass the actual cost to the companies. Our estimate is 35,000-50,000 INR per company. And we are planning to keep it free for Job Seekers (Agile Practitioners) to attend.

Exclusive Event: Get FREE agile career counseling and coaching from Naresh Jain, the founder of the Agile movement in India.

Sounds interesting? Fill the form to participate…Agile Job Fair

What is required for Deliberate or Deep Practice?

Wednesday, December 18th, 2013

There is an old saying that “Practice makes perfect“. But not all practice can make you perfect. There is a specific type of practice usually referred to as deliberate practice or deep practice which can really help you master the skill at hand.

Geoff Colvin, the author of the groundbreaking bestseller Talent Is Overrated explains that deliberate practice can be described by these five characteristics:

  1. It’s designed specifically to improve performance
  2. It can be repeated a lot
  3. Feedback on actions is continuously available
  4. It’s highly demanding mentally
  5. It isn’t much fun

Hence a short, focused and regular practice session is lot more effective than one long, generic, random practice session.

Its also important to note that deliberate/deep practice requires a certain context or mindset to really facilitate the learning. Following are important characteristics:

  • You are slightly off-balance, trying to get back
  • You are constantly getting tons of clear, instant feedback
  • You are at the edge of your ability, but motivated to stretch yourself a little bit more
  • You are staring at you role models, .i.e. you’ve a clear idea of your goals or the kind of person you want to become.

The advantage of deliberate practice is cumulative, hence starting early has a big advantage.

Generally, I used to be against rote learning, but now I’m rethinking through my conclusions.

If this topic interests you, I would strongly recommend the following books:

Some good videos to start on this topic:

Mocking only Abstract Methods using Mockito (Partial Mocking)

Thursday, December 5th, 2013

I remember back in the days, before any mocking frameworks existed in Java, we used to create an anonymous-inner class of an abstract class to fake-out the abstract method’s behaviour and use the real logic of the concrete method.

This worked fine, except in cases where we had a lot of abstract methods and overriding each of those methods to do-nothing or return dummy value seemed like a complete waste of time.

With the mocking frameworks like Mockito, we have a better way to deal with such situations, esp. in legacy code. But there is a catch. Let me explain it to you via a code example.

public abstract class AbstractClazz {
	public String sayHello() {
		return "Hello";
	}
}
 
public class AbstractClazzTest {
	@Test
	public void shouldCallRealMethods() {
		AbstractClazz clazz = mock(AbstractClazz.class);
		assertEquals("Hello", clazz.sayHello());
	}
}

This test fails, with the following error:

java.lang.AssertionError: expected:<Hello> but was:<null>
	at org.junit.Assert.fail(Assert.java:91)
	at org.junit.Assert.failNotEquals(Assert.java:645)
	at org.junit.Assert.assertEquals(Assert.java:126)
	at org.junit.Assert.assertEquals(Assert.java:145)
	at com.agilefaqs.mocking.AbstractClazzTest.
	shouldNotFakeRealMethods(AbstractClazzTest.java:20)

To make this work, We need to pass the following Answer parameter while creating the Mock:

AbstractClazz clazz = mock(AbstractClazz.class, CALLS_REAL_METHODS);

Now let’s say our requirements have evolved. Our sayHello() method should also add the person’s name and greet. Different implementations will figure out different ways to fetch the person’s name.

public abstract class AbstractClazz {
	public String sayHello() {
		return "Hello " + fetchName() + "!";
	}
 
	protected abstract String fetchName();
}
 
public class AbstractClazzTest {
	@Test
	public void shouldCallRealMethodsAndFakeAbstractMethod() {
		AbstractClazz clazz = mock(AbstractClazz.class, CALLS_REAL_METHODS);
		when(clazz.fetchName()).thenReturn("Naresh");
		assertEquals("Hello Naresh!", clazz.sayHello());
	}
}

The moment we run this test, we get the following error:

java.lang.AbstractMethodError: 
	com.agilefaqs.mocking.AbstractClazz.fetchName()Ljava/lang/String;
	at com.agilefaqs.mocking.AbstractClazzTest.
	shouldCallRealMethodsAndFakeAbstractMethod(AbstractClazzTest.java:22)

Basically, we need our mocking framework to give us a mock which allows partial mocking. Which means, for some methods we want the real methods to be invoked and for some, we want to use the fake implementation.

One way to implement this is by creating a default mock and then explicitly setting expectation on real methods.

@Test
public void shouldCallRealMethodsAndFakeAbstractMethod() {
	AbstractClazz clazz = mock(AbstractClazz.class);
	when(clazz.sayHello()).thenCallRealMethod();
	when(clazz.fetchName()).thenReturn("Naresh");
	assertEquals("Hello Naresh!", clazz.sayHello());
}

But the main problem with this approach is that we need to ensure we set explicit expectations on all public and protected methods which might be internally called by the main method (sayHello in this case.) To make matters worse, private methods can’t be mocked and hence we can’t set expectations on them. But let’s say at a later point, if someone makes a private method protected/public, the test will fail, as it will now get mocked. Overall this strategy can make your tests extremely fragile.

For example the following works:

public abstract class AbstractClazz {
	public String sayHello() {
		return "Hello " + fetchName() + closingSymbol();
	}
 
	private String closingSymbol() {
		return "!";
	}
 
	protected abstract String fetchName();
}
 
public class AbstractClazzTest {
	@Test
	public void shouldCallRealMethodsAndFakeAbstractMethod() {
		AbstractClazz clazz = mock(AbstractClazz.class);
		when(clazz.sayHello()).thenCallRealMethod();
		when(clazz.fetchName()).thenReturn("Naresh");
		assertEquals("Hello Naresh!", clazz.sayHello());
	}
}

However if we change closingSymbol() method to protected/public, the test will fail with the following error:

org.junit.ComparisonFailure: expected:<Hello Naresh[!]> but was:<Hello Naresh[null]>

A better approach in Mockito is to pass a Custom Answer parameter while creating the mock. Following is the Answer implementation which can do partial mocking:

public class AbstractMethodMocker implements Answer<Object> {
	@Override
	public Object answer(InvocationOnMock invocation) throws Throwable {
		Answer<Object> answer;
		if (isAbstract(invocation.getMethod().getModifiers()))
			answer = RETURNS_DEFAULTS;
		else
			answer = CALLS_REAL_METHODS;
		return answer.answer(invocation);
	}
}
 
public class AbstractClazzTest {
	@Test
	public void shouldCallRealMethodsAndFakeAbstractMethod() {
		AbstractClazz clazz = mock(AbstractClazz.class, new AbstractMethodMocker());
		when(clazz.fetchName()).thenReturn("Naresh");
		assertEquals("Hello Naresh!", clazz.sayHello());
	}
}

If you are forced to use this technique in brand new code you are building, may I suggest the Delete button…

Embracing Simplicity to Kill Conditional Complexity – A Real World Example

Monday, October 28th, 2013

In the Agile India Submission system, we had a feature which would accept different url path to show matching proposals.

For example:
http://present.agileindia.org/agile-india-2014/agile-lifecycle/45_mins –> will return all 45 mins proposals under the Agile-Lifecycle track for the Agile India 2014 Conference.
http://present.agileindia.org/agile-india-2014/beyond-agile/90_mins –> will return all 90 mins proposals under the Beyond-Agile track for the Agile India 2014 Conference.

If we remove the last part .i.e. http://present.agileindia.org/agile-india-2014/agile-lifecycle –> will return all proposals under the Agile-Lifecycle track for the Agile India 2014 Conference.
and http://present.agileindia.org/agile-india-2014 –> will return all proposals for the Agile India 2014 Conference (could be other conferences as well.)

To achieve this, we had the following routes defined:

//URL Path = /agile-india-2014
app\get("/{conference}", function($req) {
    $query_params = structure_request_params($req['matches']);
    ...
});
 
//URL Path = /agile-india-2014/agile-lifecycle
app\get("/{conference}/{track}", function($req) {
    $query_params = structure_request_params($req['matches']);
    ...
});
 
//URL Path = /agile-india-2014/agile-lifecycle/45_mins
app\get("/{conference}/{track}/{label}", function($req) {
    $query_params = structure_request_params($req['matches']);
    ...
});

In our database we had the following:

CREATE TABLE `conference` (
  `key` VARCHAR(255) NOT NULL,
   ...
  PRIMARY KEY  (`key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
CREATE TABLE `track` (
  `conference` VARCHAR(255) NOT NULL,
  `key` VARCHAR(255) NOT NULL,
  ...
  PRIMARY KEY  (`key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;  
 
CREATE TABLE `label` (
  `conference` VARCHAR(255) NOT NULL,
  `track` VARCHAR(255) NOT NULL,
  `key` VARCHAR(255) NOT NULL,
  ...
  PRIMARY KEY  (`conference`,`track`,`key`)
) ENGINE= InnoDB DEFAULT CHARSET=utf8;
 
INSERT INTO `conference` VALUES ('agile-india-2014', ...);
INSERT INTO `track` VALUES ('agile-india-2014', 'agile-lifecycle', ...);
INSERT INTO `label` VALUES ('agile-india-2014', 'agile-lifecycle', '45_mins', ...);

And some really complicated logic:

// For example URL Path /agile-india-2014 will result in 
// $req_params = array('conference'=>'agile-india-2014')
// And URL Path /agile-india-2014/agile-lifecycle will result in 
// $req_params = array('conference'=>'agile-india-2014', 'track'=>'agile-lifecycle')
// And URL Path /agile-india-2014/agile-lifecycle/45_mins will result in 
// $req_params = array('conference'=>'agile-india-2014', 'track'=>'agile-lifecycle', 'label'=>'45_mins')
function structure_request_params($req_params){
    $query_params = array();
    if(isset($req_params['label'])){
        if(Conference::exists($req_params['conference'])){
            $query_params['conference'] = $req_params['conference'];
            if(Track::exists(array('conference' => $req_params['conference'], 'key' => $req_params['track']))){
                $query_params['track'] = $req_params['track'];
                if(Label::exists(array(
                                       'conference' => $req_params['conference'], 
                                       'track' => $req_params['track'], 
                                       'key' => $req_params['label'])
                                       )) {
                    $query_params['label'] = $req_params['label'];
                }
                else{
                    $query_params['search_term'] = array($req_params['label']);
                }
            }
            else{
                $query_params['search_term'] = array($req_params['track'], $req_params['label']);
            }
        }
        else{
            $query_params['search_term'] = array($req_params['conference'], $req_params['track'], $req_params['label']);
        }
    }
    else{
        if(isset($req_params['track'])){
            if(Conference::exists($req_params['conference'])){
                $query_params['conference'] = $req_params['conference'];
                if(Track::exists(array('conference' => $req_params['conference'], 'key' => $req_params['track']))){
                    $query_params['track'] = $req_params['track'];
                }
                else{
                    $query_params['search_term'] = array($req_params['track']);
                }
            }
            else{
                if(Track::exists(array('key' => $req_params['conference']))){
                    $query_params['track'] = $req_params['conference'];
                    if(Label::exists(array('track' => $req_params['conference'], 'key' => $req_params['track']))){
                        $query_params['label'] = $req_params['track'];
                    }
                    else{
                        $query_params['search_term'] = array($req_params['track']);
                    }
                }
                else{
                    $query_params['search_term'] = array($req_params['conference'], $req_params['track']);
                }
            }
        }
        else{
            if(isset($req_params['conference'])){
                if(Conference::exists($req_params['conference'])){
                    $query_params['conference'] = $req_params['conference'];
                }
                elseif(Track::exists(array('key' => $req_params['conference']))){
                    $query_params['track'] = $req_params['conference'];
                }
                elseif(Label::exists(array('key' => $req_params['conference']))){
                    $query_params['label'] = $req_params['conference'];
                }
                else{
                    $query_params['search_term'] = array($req_params['conference']);
                }
            }
        }
    }
    return $query_params;
}

And supporting model classes:

class Conference{
    static function exists($conference){
        DB::query('SELECT * FROM conference WHERE conference.key = %s', $conference);
        return (DB::count() > 0);
    }
}
 
class Track{
    static function exists($query_params){
        $search_query = Track::create_search_query($query_params);
        DB::query("SELECT * FROM track WHERE $search_query");
        return (DB::count() > 0);
    } 
 
	private static function create_search_query($query_params){
        $search_query = array();
        foreach($query_params as $key => $value){
            $search_query[] = "$key='$value'";
        }
        return join(' AND ', $search_query);
    }
}
 
class Label{
    static function exists($query_params){
        $search_query = Label::create_search_query($query_params);
        DB::query("SELECT * FROM label WHERE $search_query");
        return (DB::count() > 0);
    }
 
	private static function create_search_query($query_params){
        $search_query = array();
        if(!empty($query_params)){
            foreach($query_params as $key => $value){
                $search_query[] = "$key='$value'";
            }
            return join(' AND ', $search_query);
        }
        return '';
    }
}

This was extremely hard to understand, so I decided to clean it up and get rid of the conditional complexity code smell.

Refactored code:

function structure_request_params($req_params){
    $criteria = array('conference', 'track', 'label');
    $params = array_values($req_params);
    return build_search_query_params($criteria, $params, array());
}
 
function build_search_query_params($criteria, $params, $results) {
    if(empty($params)) return $results;
    if(empty($criteria)) {
        if(!empty($params)) {
            $results['search_term'] = $params;
        }
        return $results;
    }
    if(is_present_in_db($criteria[0], $params[0])) {
        $results[$criteria[0]] = $params[0];
        array_shift($params);
    }
    array_shift($criteria);
    return map_criteria_params($criteria, $params, $results);
}
 
function is_present_in_db($table_name, $value){
    return (DB::queryFirstField("SELECT count(*) FROM $table_name WHERE `key` = %s", $value) > 0);
}

Also with this, we were able to delete the Model classes as they were not required.

Agile India 2014 Conference Draft Schedule

Friday, October 18th, 2013

Draft Schedule; only for early feedback purpose. Likely to change.

Direct Link

Please email Pramod Sadalage, Conference Program Chair with your suggestions.

Problem Solving Techniques For Evolutionary Design

Thursday, October 10th, 2013

At the Agile Goa conference, I ran a small workshop to help participants understand the core techniques one should master to effectively practice evolutionary design while solving real-world problems.

Key take aways:

  1. Eliminate Noise – Distill down the crux of the problem
  2. Divide and Conquer – Focus on one scenario at a time and incrementally build your solution
  3. Add constraints to future simplify the problem
  4. Simple Design – Find the simplest possible solution to solve the current scenario at hand
  5. Refactor: Pause, look for a much simpler alternative
  6. Be ready to throw away your solution and start again

Agile Goa 2013 Conference Program

Thursday, September 19th, 2013

Agile Goa 2013 Conference Program

Adaptive Change Cycle applied to Software Development Methods

Thursday, September 12th, 2013

Why do we see new process or methodology or movement every 10 years or so?

Using the Adaptive Change Cycle we can easily explain the rationale behind it.

Agile India 2014 Confirmed Invited Speakers as of Sep 5th 2013

Thursday, September 5th, 2013

Online Registratrions open for all 3 Agile India Conferences

Thursday, September 5th, 2013

Register for the three existing conferences coming up in India:

1. Agile Goa 2013 – Our 6th Annual Conference in Goa
We’ve a superb speaker lineup. Check out the program
Limited seats available, register today at: http://booking.agilefaqs.com/agile-goa-2013

2. Agile Kerala 2013 – FIRST ever Agile and Lean Conference in Kerala. Check out our Planned Program
Take advantage of the Early-Bird pricing, register today at: http://booking.agilefaqs.com/agile-kerala-2013

3. Agile India 2014 – Asia’s Premier and Largest conference on Agile and Lean Methods. Get an opportunity to meet Martin Fowler, Dave Thomas, Dave Snowden, Ash Maurya and many other thought leaders…

We launched the online registration on Sep 2nd at 10:00 AM. In a matter of 40 mins, the entire Super Early Bird Registration Slab of 100 tickets was completely SOLD OUT. This is the best response we’ve got in the last 9 years of organizing these conferences.

Don’t miss this unique opportunity to grab a conference ticket at these special, limited-time, attractive prices – http://booking.agilefaqs.com/agile-india-2014

    Licensed under
Creative Commons License