Monday, October 29, 2012

The Measure of a Design

We all want to write good software rather than bad software. What makes software good? I am not going to try and answer that question today. Instead I am going to talk about something that I've observed over and over in my software career - How can you judge whether software is designed well?

how easy is it to change the software?Note, I am not talking about whether the software is good or not. The ultimate metric of that is, does it solve the problem it is supposed to. My question is, is the architecture and design good? The empirical measure of that is, when new requirements come up, how easy is it to change the software?

Ease of changing software consists of various factors: how much code has to be written/changed, how easy is it to find the appropriate place(s) to change the code, and how confident can you be that the changes didn't break other functionality.

How Much Code
This measure isn't about absolute lines of code. It's about lines of code relative to expectations. If you are adding complex functionality, you'd expect it to be a decent amount of code. On the other hand, if you are just fixing a typo in an output, you'd expect that to be a really small amount of work.

There was one project I worked on that included a report of employees and their salaries. A request came in that the report have the employees sorted by salary. My expectation for this change is it should just be a matter of putting a sort call in the appropriate place, and possibly having to write a custom comparator. I.e. a really quick change. In reality, this change required some major reworking, and had to be delayed because it was going to take too long to accomplish.

When simple changes take a lot of work, this is evidence that your design is less than ideal.

Appropriate Place
When writing software, most tasks can be accomplished in a myriad of ways. A good architecture will lead you to choose one way over another. When the architecture guides you to a specific place for putting in new features, this is a good thing. Not only does it help with consistency, it means that when your new feature has to be changed, the next developer will know exactly where to go to find what you did, and know where to make the change.

This is something I really like about Ruby on Rails. It is an opinionated framework which sometimes feels very limiting, but means that most times I know exactly where to go find the code that I am looking for.

Unexpected Changes
One fear that is common when changing existing software is that you'll break something unintentionally. A hallmark of a good design is that this rarely happens. Well structured code with clean decoupled interfaces can go a long way to giving you confidence that a new change won't have unintended consequences. Yes, having a large suite of automated tests is really helpful (whether or not your design is good), but no test suite is completely thorough. I find that when I am making changes to code, I usually have a pretty good feel of how risky the change is. When most changes feel risky, this is a sign that the design can be improved.

Example
A coworker recently came to me with a change request from our client. We have an electronic form system. Form instances are allowed to be edited by the form submitter. Well, this one form type includes a list of people (as a field in the form) that the client wanted to also be allowed to edit the form. I.e. we had to make a change in the security system, but just for part of the application.

To fix this, what we had to do was to override the can_edit? method that is in the base form class, with the logic required in the derived class. Something like:
class SpecificForm < BaseForm
  def can_edit?
    super || form_editors.include?(current_user)
  end
end
The current design was a good one for this change by the above metrics. The new code required was very simple, it was obvious that "can_edit?" was the appropriate place to put the new logic, and since the change exists as a new method only in the one specific class (i.e. the base class's logic didn't change), it is unlikely to affect any other portion of the application.

Refactoring
Code is rarely written in ideal situations. Requirements aren't all known up front. You don't always have an adequate amount of time to do a good job. As a result, existing code bases don't have the best design. When you find that more and more of your changes take more time than expected, or it isn't obvious how they should be done, or each change introduces new bugs, it is time to think about refactoring.

It can be hard to find the time to do this, because by definition refactoring doesn't add any new functionality or fix any existing bugs. Yet it is important to do at times. A month ago, the "can_edit?" change mentioned above wouldn't have been nearly so simple. The permissions functionality had been spread across multiple classes and methods and instance variables in a very inconsistant manner. As such, it was easy to get confused as to the right way to do things, easy to miss a change that needed to be made, and easy to introduce bugs. However, a couple of weeks ago, I finally got fed up with all these (and many other) problems, and refactored the code. While that meant a couple of days where nothing new got fixed or added, that time will be more than made up over the next couple months as new features and bug fixes get made much more quickly.

One last thought. A, often overlooked, key to successful refactoring is pride - don't have too much pride in your code. Many times I've seen developers who are happy to refactor other people's code, but don't want to admit that there could be anything less than perfect about their own design and so resist any changes. Almost everything can be improved. Have enough security in yourself to allow your own designs to be improved and you will become a better developer.

Monday, July 2, 2012

Simple Spaceship in HTML 5

Today's post extends last week's orbit by allowing you to control the spaceship.

Demo


Mult:    Refresh: ms   


JavaScript
This code uses the same 3 classes as the previous demo. To keep the math simpler, I assume that the spaceship starts out 1 unit from the center of the solar system and has a speed of 1. Unlike the previous demo, the acceleration follows the inverse square law to improve realism.

Ship
The ship is still a position, velocity, and acceleration, but now it also has a direction and a concept of a thruster.
  1 var Ship = function() {
  2     this.pos = new Vector();
  3     this.vel = new Vector();
  4     this.acc = new Vector();
  5     this.color = "255,255,255";
  6     this.dir = 0;
  7     this.max_dir = 12;
  8     this.thrusterOn = false;
  9     this.thrusterForce = 0.1;
 10     this.size = 0.01;
 11 }
 12 
 13 Ship.prototype.turnRight = function() {
 14     this.dir = (this.dir + 1) % this.max_dir;
 15 }
 16 
 17 Ship.prototype.turnLeft = function() {
 18     this.dir = (this.dir + this.max_dir - 1) % this.max_dir;
 19 }
 20 
 21 Ship.prototype.angle = function() {
 22     return this.dir * 2 * Math.PI / this.max_dir
 23 }
 24 
 25 Ship.prototype.adjust = function(time) {
 26     if (this.thrusterOn) {
 27         this.acc.x += this.thrusterForce * Math.sin(this.angle());
 28         this.acc.y -= this.thrusterForce * Math.cos(this.angle());
 29     }
 30     this.vel.adjust(this.acc, time);
 31     this.pos.adjust(this.vel, time);
 32 }
 33 
 34 Ship.prototype.draw = function(ctx) {
 35     ctx.save();
 36     ctx.translate(this.pos.x, this.pos.y);
 37     ctx.rotate(this.angle());
 38     ctx.strokeStyle = "rgb(" + this.color + ")";
 39     ctx.scale(this.size, this.size);
 40     ctx.beginPath();
 41     ctx.moveTo(0, -10);
 42     ctx.lineTo(5, 10);
 43     ctx.lineTo(-5, 10);
 44     ctx.lineTo(0, -10);
 45     ctx.stroke();
 46 
 47     if (this.thrusterOn) {
 48         ctx.fillStyle = "rgb(" + this.color + ")";
 49         circle(ctx, 0, 11, 1);
 50         circle(ctx, -2, 13, 1);
 51         circle(ctx, 2, 13, 1);
 52         circle(ctx, -4, 15, 1);
 53         circle(ctx, 4, 15, 1);
 54         circle(ctx, -1, 15, 1);
 55         circle(ctx, 1, 15, 1);
 56     }
 57     
 58     ctx.restore();
 59 }
Simulation
The simulation has the same responsibilities as before, but also scales the display so that a radius of 1 in ship space corresponds to halfway out on the canvas.
  1 var Simulation = function(ctx, size) {
  2     this.ctx = ctx;
  3     this.size = size;
  4     this.reset();
  5 }
  6 
  7 Simulation.prototype.drawStar = function() {
  8     this.ctx.fillStyle= "#FF0";
  9     circle(this.ctx, 0, 0, 20);
 10 }
 11 
 12 Simulation.prototype.draw = function() {
 13     this.ctx.fillStyle = "rgba(0,0,0,1.0)";
 14     this.ctx.fillRect(0, 0, this.size, this.size);
 15     
 16     var dim = this.size/2;
 17     
 18     this.ctx.save();
 19     this.ctx.translate(dim, dim);
 20     this.drawStar();
 21     this.ctx.scale(dim/2.0, dim/2.0);
 22     this.ship.draw(this.ctx);
 23     this.ctx.restore();
 24 }
 25 
 26 Simulation.prototype.step = function() {
 27     var ship = this.ship;
 28     var acc = ship.acc;
 29     var angle = Math.atan2(ship.pos.x, ship.pos.y);
 30     var d2 = ship.pos.x*ship.pos.x + ship.pos.y*ship.pos.y;
 31     acc.x = -Math.sin(angle) / d2;
 32     acc.y = -Math.cos(angle) / d2;
 33     ship.adjust(this.mult);
 34     if (d2 > 16) {
 35         this.stop();
 36         alert("Ship Left Orbit!");
 37     }
 38 }
 39 
 40 
 41 Simulation.prototype.reset = function() {
 42     this.stop();
 43     var ship = new Ship();
 44     ship.pos.x = 1.0;
 45     ship.pos.y = 0.0;
 46     ship.vel.x = 0.0;
 47     ship.vel.y = -1.0;
 48     this.ship = ship;
 49 }
 50 
 51 Simulation.prototype.run = function(mult, refresh) {
 52     this.stop();
 53     this.mult = mult;
 54     var self = this;
 55     this.timer = setInterval(function() {
 56         self.step();
 57         self.draw();
 58     }, refresh);
 59 }
 60 
 61 Simulation.prototype.stop = function() {
 62     clearInterval(this.timer);
 63 }
Helper Function
Both the above classes call the helper function "circle" for drawing filled in circles.
  1 var circle = function(ctx, x, y, rad) {
  2     ctx.beginPath();
  3     ctx.arc(x, y, rad, 0, 2*Math.PI, false);
  4     ctx.fill();
  5 }
Onload
We need a few more controls, so the onload portion has a little extra functionality.
  1 $(function() {
  2     var canvas = document.getElementById("canvas");
  3     var size = 500;
  4     canvas.width = size;
  5     canvas.height = size;
  6     var ctx = canvas.getContext('2d');
  7     var sim = new Simulation(ctx, size);
  8     sim.draw();
  9 
 10     $("#run").click(function() {sim.run($("#mult").val(), $("#refresh").val())});
 11     $("#stop").click(function() {sim.stop()});
 12     $("#reset").click(function() {sim.reset(); sim.draw();});
 13     $("#ccw").click(function() {sim.ship.turnLeft(); sim.draw();});
 14     $("#cw").click(function() {sim.ship.turnRight(); sim.draw();});
 15     $("#thruster").mousedown(function() {sim.ship.thrusterOn = true; sim.draw();});
 16     $("#thruster").mouseup(function() {sim.ship.thrusterOn = false; sim.draw();});
 17 });
HTML
Again, the HTML is just a canvas along with some controls.
  1 <canvas id="canvas"></canvas>
  2 <br />
  3 Mult: <input id="mult" type="text" value="0.01" size="4" />
  4   
  5 Refresh: <input id="refresh" type="text" value="15" size="3" />ms
  6   
  7 <button id="run">Run</button>
  8 <button id="stop">Stop</button>
  9 <button id="reset">Reset</button>
 10 <br />
 11 <button id="ccw">CCW</button>
 12 <button id="cw">CW</button>
 13 <button id="thruster">Thruster</button>

Monday, June 25, 2012

Going in Circles via Html5

Today's post is just a simple little HTML5 animation using canvas that moves a spaceship in orbit around a star (not to scale).

Demo

Mult:    Refresh: ms   

JavaScript
This may be over-engineering for such a simple program, but there are 3 javascript classes used to accomplish this.

Vector
(not a Java Vector but a math vector)
  1 var Vector = function(x, y) {
  2   if (arguments.length == 0) {
  3     x = 0.0;
  4     y = 0.0;
  5   }
  6   this.x = x;
  7   this.y = y;
  8 };
  9 
 10 Vector.prototype.adjust = function(v, mult) {
 11   this.x += v.x * mult;
 12   this.y += v.y * mult;
 13 }

Ship
A ship consists of 3 vectors (position, velocity, and acceleration), and also knows how to draw itself.
  1 var Ship = function() {
  2     this.pos = new Vector();
  3     this.vel = new Vector();
  4     this.acc = new Vector();
  5     this.color = "255,255,255";
  6 }
  7 
  8 Ship.prototype.adjust = function(time) {
  9     this.vel.adjust(this.acc, time);
 10     this.pos.adjust(this.vel, time);
 11 }
 12 
 13 Ship.prototype.draw = function(ctx) {
 14     ctx.save();
 15     ctx.translate(this.pos.x, this.pos.y);
 16     ctx.strokeStyle = "rgb(" + this.color + ")";
 17     ctx.beginPath();
 18     ctx.moveTo(0, -10);
 19     ctx.lineTo(5, 10);
 20     ctx.lineTo(-5, 10);
 21     ctx.lineTo(0, -10);
 22     ctx.stroke();
 23 
 24     ctx.restore();
 25 }

Simulation
A simulation consists of the ship, knows how to accelerate the ship around in an orbit, and has the commands for actually starting, stopping, and running the simulation.
  1 var Simulation = function(size) {
  2     this.size = size;
  3     this.reset();
  4 }
  5 
  6 Simulation.prototype.drawStar = function(ctx) {
  7     ctx.fillStyle= "#FF0";
  8     ctx.beginPath();
  9     ctx.arc(0, 0, 20, 0, 2*Math.PI, false);
 10     ctx.fill();
 11 }
 12 
 13 Simulation.prototype.draw = function(ctx) {
 14     ctx.save();
 15     ctx.fillStyle = "rgba(0,0,0,1.0)";
 16     ctx.fillRect(0, 0, this.size, this.size);
 17     ctx.translate(this.size/2, this.size/2);
 18     this.drawStar(ctx);
 19     this.ship.draw(ctx);
 20     ctx.restore();
 21 }
 22 
 23 Simulation.prototype.step = function() {
 24     var ship = this.ship;
 25     var acc = ship.acc;
 26     acc.x = -ship.pos.x;
 27     acc.y = -ship.pos.y;
 28     ship.adjust(this.mult);
 29 }
 30 
 31 
 32 Simulation.prototype.reset = function() {
 33     this.stop();
 34     var ship = new Ship();
 35     ship.pos.x = this.size / 4;
 36     ship.pos.y = 0;
 37     ship.vel.x = 0;
 38     ship.vel.y = -this.size / 4;
 39     this.ship = ship;
 40 }
 41 
 42 Simulation.prototype.run = function(ctx, mult, refresh) {
 43     this.stop();
 44     this.mult = mult;
 45     var self = this;
 46     this.timer = setInterval(function() {
 47         self.step();
 48         self.draw(ctx);
 49     }, refresh);
 50 }
 51 
 52 Simulation.prototype.stop = function() {
 53     clearInterval(this.timer);
 54 }

On Load
Then there is the little bit of code to kick everything off, which leverages jQuery. This just sets the size of the canvas, and links the input controls from the HTML to the Simulation object.
  1 $(function() {
  2     var canvas = document.getElementById("canvas");
  3     var size = 500;
  4     canvas.width = size;
  5     canvas.height = size;
  6     var ctx = canvas.getContext('2d');
  7     var sim = new Simulation(size);
  8     sim.draw(ctx);
  9 
 10     $("#run").click(function() {sim.run(ctx, $("#mult").val(), $("#refresh").val())});
 11     $("#stop").click(function() {sim.stop()});
 12     $("#reset").click(function() {sim.reset(); sim.draw(ctx);});
 13 });

HTML
The HTML is just a canvas along with some inputs to control the simulation.
  1 <canvas id="canvas"></canvas>
  2 <br />
  3 Mult: <input id="mult" type="text" value="0.01" size="4" />
  4   
  5 Refresh: <input id="refresh" type="text" value="15" size="3" />ms
  6   
  7 <button id="run">Run</button>
  8 <button id="stop">Stop</button>
  9 <button id="reset">Reset</button>

Monday, May 7, 2012

Thoughts on Interviewing

I've been on both sides of the interviewing table a number of times. Currently I'm on the less stressful side, that of trying to find a good candidate. So how do you find a good candidate?

Joel on Software has two criteria: Smart, and Gets Things Done. His Guerrilla Guide to Interviewing does a good job of explaining what he means, so I won't repeat it. The question is, how do you determine if someone is smart and/or gets things done?

The Google/Facebook style of interviewing where you have to solve problems on a whiteboard answers the "smart", at least for a certain type of smart, and assuming that you don't freeze up under the pressure of interviews. But what about "gets things done"? The ability to solve problems doesn't mean that you will work diligently. So the best way I know to determine that is to investigate your track record. An internship or trial hire period would be better, but since that isn't typically an option, it instead means trying to figure out from someone's resume and their answers to questions about it. This is very much, an inexact science.

What about smart? Different positions require different degrees of intelligence. As my boss reminded my, the work that we currently do doesn't require super genius intelligence. However, I maintain that no matter how mundane the job is, it is better to have higher caliber employees doing them. Do you really want to be maintaining the software of someone who is just barely qualified to do the job? Are you really going to go to that person for help when an issue arises? How are you going to learn and improve while working with someone who just wants to get by?

Therefore, while I am not looking for super-geniuses, I do want someone who is more than just barely competent. So how do I personally test for that?

Based on Joel's article we now make candidates write some programs on the whiteboard. We are hiring a programming position. If you want the job you should be capable of turning thoughts and ideas into code. I realize that between modern IDEs and the internet, modern day programmers have access to unprecedented levels of help. However, you shouldn't be dependent on that help. If you don't know the difference between AND and OR without looking it up, or you don't know any collection types other than an ArrayList, how can I be confident that you will write quality maintainable code?

How about theoretical knowledge from a CS program? How important is that? I realize how much importance you put on that will depend on your own skills and experiences. While it may not be important to actually know that the time complexity of a bubble sort in O(n2), at some point you will run into code that is running unacceptably slow. If you don't show any knowledge of time complexity, what evidence do I have that you will actually pick out the bottlenecks of a slow program and know how to fix them? (or even better, how to avoid them in the first place?)

Unfortunately for me, every example above came from recent interviews I've done. Am I wrong to think that a Java developer should be familiar with more than just ArrayList from the java.util Collection classes? Is it random trivia to remember the time complexity of bubble sort? I think these are examples of things people who care about the craft of software development should know, but maybe I am biased by my own experiences?

What do you think?

Monday, April 16, 2012

Saved By The VM

One of the most exhilarating, but also frustrating, aspects of working in computers is how fast things move. Things that would've been considered disgracefully wasteful years ago, now provide us with solutions that I never would've imagined then. For example the editor that I am using to write this is running inside of my browser. In effect, the browser is acting as a virtual machine, and the editor is just one application running on it. How much CPU "wastage" is there by running in a VM rather than a native application? Who knows, but it doesn't matter, as my laptop isn't even breaking a sweat. And this is just one of dozens of tabs that I have open in the browser, and the browser is just one of over a dozen applications running currently (plus who knows what the OS is doing).

Not only is CPU time cheap, but so is storage. At work, our network storage takes automatic snapshots, which makes it trivial to retrieve from backups. This would've seemed incredibly wasteful to me not that long ago. This, combined with VMs, saved me a ton of work this past week.

Torquebox 2.0.0 was released recently, and I wanted to get it installed on our development server. While I was doing this, I decided to upgrade the version of Java running on the machine. I downloaded the latest version and installed it. Unfortunately, I wasn't paying attention and I let it install in the default location, rather than our group's standard location. In a moment when I was apparently flashing back to the days of yore, I thought I could just move the Java directory structure to the location where I wanted it. Since the registry settings did not get updated in the process, bad things happened. My first attempt to fix things was to just delete the Java directories, and reinstall it. Unfortunately, Java thought it was still installed, and refused to reinstall. So then I asked Windows to uninstall Java, but a DLL was missing (probably because I had already deleted it), so it couldn't uninstall either.

What was I to do? I spent some time fiddling with the Windows Registry, but that was getting me nowhere. Finally, I came to my senses and remembered what year it was. Our servers are running on VMs which means it's just an image on a network disk somewhere, which has regular snapshots taken. So I just wandered down the hall and asked one of our system administrators to restore the VM to an image from the day before. A few minutes later, my mess was cleaned up.

So yes, VMs are wasteful and inefficient, but they sure save time sometimes. And what's all this extra CPU power for, if not to make our lives easier?

Monday, April 2, 2012

Pair Programming - My Experiences

I've mentioned pair programming before in the context of the psychology of extreme programming, but I haven't said too much about it. This is because I've had very little first hand experience with it. Well, in the last couple of months I've spent a lot of my time pair programming, and here are my preliminary thoughts.

Distractions
I have argued that pair programming can cut down on distractions. I find this to be true. When working on my own if I execute a command that I know will take 5 or more seconds, it is very tempting to take this break to check my email or read a slashdot article. Unfortunately, while I justify this as being efficient, in reality it turns a 5-10 second pause into a couple minute pause. The change of context also flushes some registers, err.. short term memory, making it harder to be as focused when I first switch back to my development tasks.

When someone is looking over my shoulder, I am not going to make that context switch.

Motivation
I find that my enthusiasm for projects ebbs and flows. I'll be gung-ho for a while, and then my motivation will slip away. Its easier to keep working in a pair, both from the peer-pressure that you don't want to be slacking, and just the fact that its more fun.

Efficiency
I've always read that pair programming is more productive than one person working alone, but not twice as productive. (measured in terms of how much code gets written per unit time). My experience of the past couple months agrees with this. Of course this ignores other benefits from pair programming, like potentially higher quality code, and the implicit cross-training that happens.

Correctness
Pair programming definitely cuts down on mistakes. While the person on the keyboard is directly thinking about what they are doing, the backseat driver can focus on this big picture. These two different views helped us realize shortcomings that would not have been found until production had either of us been working alone. Also, when bugs were encountered, having two people (who think differently) looking at them, meant we were able to resolve them quicker.

Drawbacks
There are a few problems that I encountered with pair programming. The most obvious one is scheduling. The two of us paired up have different schedules in our personal lives, so we don't show up at the same time, or leave at the same time, or even take our lunch breaks at the same time. Plus we both have other professional responsibilities that sometimes require one or the other's attention at various times at the day. Thus an eight hour work day results in only four or five hours of useful pairing time.

The problem that I didn't expect was energy drain. After a couple of weeks of doing primarily pair programming I felt mentally exhausted. While we got a lot done, it wasn't a pace that I can maintain indefinitely. I think the little breaks that I normally build into my day, which were missing when pair programming, helped keep me fresh. I suspect that I can address this problem by intentionally scheduling breaks when pairing up.

it is most effective to work alone for a whileThe third, and most important, problem is problem solving. There are obviously times when two heads are better than one. But there are also times where the two heads need to work on their own. To solve difficult design problems, or to come up with innovative solutions to a problem, I truly believe it is most effective to work alone for a while. Then, after I've had a chance to think about it in my own way and come to my own conclusions, is the time to meet and discuss the ideas with others. i.e. if we work together on a solution, we will come up with one solution. If we work independently, we will come up with two solutions, from which we can then draw a third solution that is better than what either of us came up with independently, or if we had worked together all along.

Conclusion
Pair Programming has a place in your toolkit of software development skills. I think it can be really helpful when working on software that you want to be reliable, you want more than one person intimately familiar with the code base,  and which doesn't require too much innovative problem solving. However, like most things, moderation is the key. There are definitely times when pair programming will hinder you.

If you have the opportunity, give it a try. However, be open to the fact that it isn't best for all situations, so don't force it if it doesn't fit.

Monday, October 31, 2011

Chasing Technology

Maybe I'm just getting old, but keeping up with technology seems to be getting harder and harder. As I've mentioned before, I am a big fan of learning your libraries, languages, and tools. But how are you supposed to do that, when the libraries keep upgrading and there are constantly new tools to use? Just this past week there was a post on slashdot suggesting that tech skills have a 2 year half life. The comments on that thread seem to mostly disagree with the hypothesis, but there is definitely some truth to it.

The two languages that I use the most at work did not exist when I started college. However, languages are easy to keep up with. They tend to move slowly and are very well documented. Tools, libraries, and frameworks, on the other hand, are a nightmare to chase. They are constantly being upgraded, and often times the best documentation are the question/answers out there on the web at places like stackoverflow. The problem with these sites is that they are only useful after a tool has been out long enough for a critical mass of people to use.

So what's a person supposed to do?

Well, first, as many commenters from the slashdot article stated, the most valuable skills a software engineer has is knowing how to design, develop, and debug software. These skills don't go away just because you are using a new language or library. So play to your strengths. If you design and write clean code, it'll be easier to fix/update as you learn the language/library/tool better.

Second, choose your upgrades carefully and intentionally. Just because there is a newer version of something out there, doesn't mean you need to use it. Are the benefits worth it to upgrade now? Does it make more sense to wait until it has been popularly adopted? Or maybe it makes sense to skip a version or two.

Third, don't become an expert in everything. Most of us have finite amounts of energy and time. So, while I'm a big believer in being an expert in the tools you are using, that isn't always practical. If a particular tool/library/framework is bound to be upgraded/replaced in the near future, it's probably ok if you use the existing library in a less than optimal way. Focus your energies on the timeless (design and coding skills) or the long lasting (languages).

constantly learnFourth, and most importantly, constantly learn. If your employer doesn't provide opportunities for professional growth, take it anyway. Just as you don't ask for permission for every single test that you write, or every whiteboard design that you draw, you don't need permission to explore new technologies and tools. Whether they know it or not, your employer pays you to be a good and competent software developer. This requires you to be constantly learning new and better ways of doing things. If you are not taking ten to twenty percent of your time exploring and learning, you are shortchanging yourself and making yourself obsolete. I realize that it seems there is never time to do this, but like other aspects of good software engineering (appropriate design, testing, documentation), in the long run you will be worse off if you don't make the investment now.