Usually I check in whenever I add something new, but try to separate stuff in discrete commits.
That means, if I add a new dependency, I make the changes until they either compile, or they are big-enough that it would be lost time to do them again, from scratch. If I have a bigger task, I try to commit multiple times, when it makes sense (once per function, every time I make it compile and run successfully, etc).
I also commit when I want a backup point (i.e. "if what I try now won't work or becomes too complicated, I want to come back to the code as it is now", or when somebody asks me to drop what I am doing and fix some urgent issue).
If you use a centralized source control system, you cannot commit arbitrarily for backup points, because a commit that doesn't compile/work affects everybody in your team.
Usually when I start adding boilerplate code (e.g. add a new webapp in a django website), I commit every operation I do.
If I follow a tutorial to generate/write code, I use step names in the tutorial for commit messages. This way, I can diff revisions and see what a tutorial step did, at any later point.
Let's say, for example, I have a project which consists of a single code file. It will take about 10 lines of boilerplate code, and 100 lines to get the project working with extremely basic functionality (1 or 2 features).
It would depend on how difficult adding the stuff is:
if it were trivial to add the boiler plate code, I would add it and commit right before starting on the other code (this way, if I make a mistake or introduce a weird bug later, I can simply revert to the boilerplate code and start again).
If the code were non-trivial, I would commit every time I added something new (anywhere between every two lines of code changed, to a hundred or so).