tl;dr
If you rely on asking, you're asking for trouble. Instead:
- Tell functions what to do, don't make them ask.
- Tell processes what to do, don't make them ask.
- Keep all your environment variable queries in one place, apart from the rest of the code.
You probably think this is obvious, but it isn't
The principle of asking versus telling has many faces in programming. Some of the faces are obvious—others are more subtle. This articles moves from the more obvious to the more subtle.
So if you find yourself saying, "Well, duh!" Keep reading.
Telling v. Asking
This Python code illustrates telling:
# teller.py
import sqlite3
def connectToDatabase(filename):
return sqlite3.connect(filename)
The connectToDatabase
function accepts an argument for the database connection details. Other code that calls connectionToDatabase
tells the function what it wants to do.
This Python code illustrates asking:
# asker.py
import sqlite3
DATABASE_FILENAME='/tmp/database.sqlite'
def connectToDatabase():
return sqlite3.connect(DATABASE_FILENAME)
The connectToDatabase
function in the above snippet is not told database connection details. Instead, the function asks for the connection details—in this case, it asks from the global scope (which is a bad place to be in).
Telling > Asking
Telling, as described above, is better than Asking for the following reasons:
- The code is more flexible for reuse.
I can more easily connect to different databases using the
teller.py
. - The code is easier to test.
Because
teller.py
is more flexible for reuse, I can use the code in tests very easily. - It's easier to know how to use the code and harder to use incorrectly.
It's obvious in
teller.py
that I must provide a filename to connect to (because of the argument spec of the function). I can't accidentally connect to/tmp/database.sqlite
.To use
asker.py
I must know that the function looks atDATABASE_FILENAME
either from reading the source code or the docstring of the function (which is absent in this case). This would be much more difficult to do ifconnectToDatabase
called other functions in other files which accessed a global variable.
Abusing Environment Variables
In asker.py
the function asks for information from the global scope. The global scope is just one place to get information from. The environment is another. Take a look at this:
# asker-env.py
import sqlite3
import os
def connectToDatabase():
return sqlite3.connect(os.environ['DATABASE_FILENAME'])
Instead of asking for the database filename from the global scope, asker-env.py
asks the environment of the process for the database filename. This is more reusable that asker.py
but is still not as good as teller.py
because:
- it still suffers from problem #3: that you must rely on the docstring or reading the source code to understand how to use it, and
- you can only have one connection per process.
A better approach would be to tell the process which database filename to use as in this example:
# teller-cli.py
import sqlite3
import argparse
def connectToDatabase(filename):
return sqlite3.connect(filename)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('filename', help="Filename of the SQLite database")
args = parser.parse_args()
connectToDatabase(args.filename)
teller-cli.py
is better than asker-env.py
because you can ask it (using
) what you need to tell it instead of having to read docstrings or source code. Having a --help
option which fully describes configuration options and is enforced when running the process is similar to teller.py
having an argument spec that is enforced by Python.
But I thought environment variables were good...
(this is a picture of an environment -- much better than the one on your computer)If you subscribe to The Twelve-Factor App's ideas, you will store all your configuration in environment variables. Or if you use Travis-CI or Heroku you will also have used environment variables to great effect. Environment variables seem like a great way to do configuration.
Environment variables are cross-language and easy to change. They have huge benefits. Environment variables are a great way to do configuration! It would be nice to leverage the great qualities of environment variables along with the great qualities of code that is told.
You can!
Convert Asking to Telling
To write Telling code that also uses environment variables, restrict the environment variable querying to a single, documented place. Consider this:
# env-runner.py
import argparse
import os
# Define ALL the environment variables this process might use.
env_vars = [
('DATABASE_FILENAME', 'Filename of the SQLite database.'),
]
# Read the environment. This function must only be called from within this
# module if you want to prevent writing asking code.
def getArgs(environ, config):
ret = {}
for (env_name, description) in config:
try:
ret[env_name] = environ[env_name]
except KeyError:
print 'Missing env var: %s %s' % (env_name, description)
raise
return ret
def main():
from teller import connectToDatabase
args = getArgs(os.environ, env_vars)
db = connectToDatabase(args['DATABASE_FILENAME'])
# ...
The above snippet has all the benefits of telling code and the flexibility of asking code:
- The code is flexible for reuse.
- The code is easy to test.
- It's easy to know how to use this code and hard to use incorrectly.
Real-world examples
As proof that the concept of Telling instead of Asking is not obvious, here are some real-world examples (both good and bad):
Klein's improvement on Flask
Flask, a micro web framework for Python, loves the global scope. This is the Hello, World! from the front page:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run()
And this is the recommended way for accessing a database:
from flask import g
app = Flask(__name__)
@app.before_request
def before_request():
g.db = connect_db()
@app.route("/")
def hello():
db = g.db
# ...
You use a package-global named g
, which is "magical" and comes with appropriate warnings:
We store our current database connection on the specialg
object that Flask provides for us. This object stores information for one request only and is available from within each function. Never store such things on other objects because this would not work with threaded environments. That specialg
object does some magic behind the scenes to ensure it does the right thing. http://flask.pocoo.org/docs/tutorial/dbcon/
Consider the improvement offered by Klein, a similar micro web framework. With Klein you can easily make apps with Non-global state:
from klein import Klein
class MyApp(object):
app = Klein()
def __init__(self, db_connection):
self.db_connection = db_connection
@app.route('/')
def hello(self, request):
db = self.db_connection
# ...
There is no magical, global g
here. You can instantiate MyApp
with a database connection, or even have three different instances of MyApp
with three different database connections all running in the same app.
Klein lets you tell instead of ask.
Ansible
Ansible is a (really good) configuration management tool. It's mostly straightforward, but it's easy to use it in an asking way—which becomes unmaintainable.
For instance, if in one of our tasks we want to download a resource from http://dev.example.com
if we're in the development environment or from https://production.example.com
if we're in the production environment, Ansible easily lets us do this:
# main.yml
- name: Get the files
command: wget {{ source_server }}/thefile.tgz /tmp/thefile.tgz
creates=/tmp/thefile.tgz
The command asks for source_server
. This task likely lives in a role's task file, which could be in roles/mymachine/tasks/main.yml
, deep within the directory structure of my configuration. The problem is that I have no way of knowing (short of manually parsing the task file) when writing my inventory file or anything else that uses/includes main.yml
, that source_server
is a needed variable.
Ansible lets you ask yourself into an unmaintainable hole. To be more maintainable, Ansible should provide a mechanism for specifying the parameters needed by tasks. Perhaps something like:
# main-with-vars.yml
- variables:
- name: source_server
description: URL of the server to download source files from. For
example: http://foo.com
default: http://example.com
- name: Get the files
command: wget {{ source_server }}/thefile.tgz /tmp/thefile.tgz
creates=/tmp/thefile.tgz
Such a file would allow you to produce a list of all the configurable variables for a role/task and then be able to tell instead of ask.
AngularJS
AngularJS does a lot to help you avoid asking through dependency injection.
Twisted's Reactor
Twisted is currently working toward making the reactor not global in an effort to make testing easier and perhaps allow for new features.
Conclusion
In conclusion, read the tl;dr at the top :) Also, do you have some example of telling v. asking? Or counter-arguments? Post a comment.
This seems like a good, specific example of how to keep your code and configuration loosely coupled. By definition, your "ask" examples require you to understand the code much more intricately in order to write your config (making them tightly coupled). Nice writeup.
ReplyDelete
ReplyDeleteThanks for sharing the good information and post more information.Talent flames company is one of the best training and placement company in Hyderabad. Providing training on Technologies like Java,Sql,Oracle,..,etc with 100% Placement Assistance. Now Interviews going on if you want to attend just visit our website and drop your resume. for more information visit us http://talentflames.com/
training and placement company in Hyderabad
ReplyDeleteThank you for giving the information and it is useful for me. training with placement company in Hyderabad
nice blog, thanks for sharing this information to us
ReplyDeleteaws training center in chennai
aws training in chennai
aws training institute in chennai
best python training in chennai
best python training in sholinganallur
best python training institute in omr
python training in omr
Today Telugu news updates provide us the information of breaking news and live updates. we get live news, political, education, technology, etc. Today Telugu news gives the best news updates. It also keeps its readers informed about the latest happenings in the world with instant updates.
ReplyDeleteVery knowledgeable post thank you for sharing...
ReplyDeleteAngularJS Training in Bangalore | AngularJS Course Fees | AngularJS 6 - i Digital Academy - AngularJS Training in Bangalore - Learn AngularJS 6 from Expert Real-time Trainers
at i Digital Academy with Live Projects and Placement Assistance. Book a Free Demo Today.
There are a lot of Love Calculator, True love calculator, love meter, love crush calculator and much more but only a few are authentic. True Love Calculator Soulmate provides a very authentic and accurate love percentage. It calculates love percentage by the couple's name. I have experienced on this site and it is a genuine Friendship calculator.
ReplyDeleteGreat experience, so speedy, sensible and uncommonly obliging and pleasant experience, clear credits, would firmly propose.
ReplyDeletePersonal Loans for Bad credit
Loans for Bad credit
Exceptionally simple to apply. Also lower interest than any remaining credit suppliers. I would energetically suggest
ReplyDeleteOnline Loans for Bad credit
Loans for people with bad credit
Truly content easily of administration - no off-kilter calls and took care of totally on the web
ReplyDeleteBad Credit Loans Personal
Bad Credit Loans for people
Fast, basic and besides online ID affirmation
ReplyDeleteCar Loans for Bad credit
Bad Credit Loans
The site is requesting the greater part from the requests that they need to grasp our money related conditions. We are happy to share my experience.. Continuously suggested!!No check credit loan
ReplyDeleteInstallment Loans for Bad credit
I applied for the explanation I would be turned down regarding credit. They ended up underwriting the credit. I was so bright. The strain I had is gone! Much gratitude to you, Allbadcreditloan, for the chance to pull together!
ReplyDeleteLoans for Bad Credit Online
Loans for Bad credit score
From my basic application for development to when the leasers were paid, I was glad about how smooth and bare it was. It has dropped a mind-boggling load from my shoulders. I would unequivocally recommend it to anyone expecting to join Mastercards.
ReplyDeleteAuto Loans for Bad credit
Urgent Loans for Bad credit
Getting a development was quick and direct. This is my fourth development, and no issues until now.
ReplyDeleteSmall Loans for Bad credit
Home Loans for Bad credit
I found that working with allbadcreditloan was just comparably basic as pie! they were incredibly obliging and given answers for all of my requests!
ReplyDeleteCar Loans for Bad credit
Bad Credit Loans
This is the second time I have helped an advance through allbadcreditloan. The collaboration is particularly fundamental and the development analyst makes the decision basic on the sum to obtain, the portion and stretch of time for the development.
ReplyDeleteBad Credit Loans Personal
Bad Credit Loans for people
This was so basic and quick . The application online was non muddled and didn't need transferring archives
ReplyDeleteOnline Loans for Bad credit
Loans for people with bad credit
It was exceptionally simple to help the credit through Allbadcreditloan. The reps were exceptionally useful through the entire cycle.
ReplyDeletePersonal Loans for Bad credit
Loans for Bad credit
artificial intelligence internship | best final year projects for cse | internship certificate online | internship for mba finance students | internship meaning in tamil
ReplyDeleteThe interaction to acquire a credit with allbadcreditloan was the best I've encountered so far. I endeavored to acquire a credit with my 2 individual banks and was denied. I was anxious to approve of them however they made the cycle so natural. I felt open to going ahead with the credit acknowledgment.
ReplyDeleteNo check credit loan
Installment Loans for Bad credit
Everything was explained thoroughly. I went through the process late on a Friday afternoon and by the following Tuesday my funds were in my account. Just in time pay for my roof replacement! I do have
ReplyDeleteLoans for Bad Credit Online
Loans for Bad credit score
The experience from meeting all requirements to getting the assets was exceptionally simple and FAST! The internet based advances were obvious. I followed the bearings in general, talked with client assistance to deal with business confirmation and settling my credit arrangement and presto I was finished!
ReplyDeleteAuto Loans for Bad credit
Urgent Loans for Bad credit
Amazing, allbadcreditloan truly got this one right! It was extremely simple to finish the application and the subsidizing after endorsement was quick. Incredible rate and no charge? This one is a champ. Much obliged, allbadcreditloan !
ReplyDeleteSmall Loans for Bad credit
Home Loans for Bad credit
Thankful for sharing your contemplations. I like your undertakings, and I will be holding on to your further surveys. Much obliged to you before long.
ReplyDeletePersonal Loans for Bad credit
Loans for Bad credit
You're so cool! I don't acknowledge that I've scrutinized anything like that beforehand. So uncommon to track down somebody for explicit extraordinary thoughts on this subject. Without a doubt.. thankful for terminating this up. This website is one thing that is expected on the web, someone with some development!
ReplyDeleteBad Credit Loans Personal
Bad Credit Loans for people
awe-inspiring idea Much obliged to you for the information; it will be advantageous for every one of us and don't stop giving additional information.
ReplyDeleteCar Loans for Bad credit
Bad Credit Loans
Thank you for giving the great article. It delivered me to understand several things about this concept. Keep posting such surpassing articles so that I gain from your great post.
ReplyDeletepython internship | web development internship |internship for mechanical engineering students |mechanical engineering internships |java training in chennai |internship for 1st year engineering students |online internships for cse students |online internship for engineering students |internship for ece students |data science internships
Hi very informative blog. Got to know a lot. You have written it precisely. Thanks a lot for sharing this information.
ReplyDeleteData Analytics Courses in Kenya
This Blog have great info about Coding language and site setting for homepage themes.
ReplyDelete