Keep API simple
I want to share a success story of designing a simple API, when the problem seemed to be complex at first glance.

Recently we got a task. We had to log every action that user does on the web. In other words, we would need a simple class (API) which could be easily used from almost all the controllers in our web. Additionally, we should log different parameters depending on action.We also have got a draft solution for this task from unknown developers. We are not going to look inside logging; we are interested in how easy is to use logging (it’s API). Below you can see a typical controller code for logging user action:
private void logUserAction(User user) throws Exception {
UserActionModel model = new UserActionModel();
model.setAction("Buy a ticket");
List<String> values = new ArrayList<String>();
values.add("PersonCode");
values.add("UserName");
values.add("ContactInformation.EmailAddress");
values.add("ContactInformation.Language");
model.setParams(LogUtil.getParamsWithFieldNames(user, values));
LogUtil.log(model);
}
Probably you guess how LogUtil.getParamsWithFieldNames works. Given a “user” object and list of Strings, it calls corresponding getters: user.getPersonCode(), user.getUserName() etc.
It’s it nice?
Look what a mature, multi-purpose solution! It doesn’t depend on concrete class - it could be User, Client, Customer or whatever else. You can pass any object and list of field names, and this universal LogUtil can log all these fields. Yeah, that’s a smart API!
But
But you know what? You just don’t need this smartness.Stop for a while and think: couldn’t it done easier? Of course it could!Why do you need reflection? Why loose compile-time check of getter names? Why bother with handling reflection (checked) exceptions? Dude, just use getters!
Finally
The final solution didn’t use reflection and was not smart - it was simple.The following is the typical controller code for logging user action:
Action action = new Action("Buy a ticket")
.add("PersonCode", user.getPersonCode())
.add("PersonName", user.getPersonName())
.add("EmailAddress", user.getContactInformation().getEmailAddress())
.add("Language", user.getContactInformation().getLanguage());
LogUtil.log(action);
As simple as possible.
Archived comments
Ivo 2011-10-19T12:24:13.531Z
Why would you implement this programmatically anyway? IMHO this is a clear case for aspect usage …
Our recent stories
Building for the future of insurance: our work on Swedbank P&C insurance claims
Our founders wrote the core of this internet bank in 2006-2007. Twenty years later, we're back inside it.
Flowers for a new generation - live in one month with AI as the pair programmer
How a long-time client's idea for reaching younger customers became our first project built systematically with AI from end to end - prototype, code, and content - while keeping every line reviewed and owned by us.
A bank built in 8 months - licence confirmed, deadline met
Build or buy? When the client came to us, that question was still open. We recommended building. No vendor lock-in, no paying for features you'll never use, no waiting on someone else's roadmap. A codebase you own. A product you control. They agreed. That put more weight on us - and we were fine with that.