The concept of black-box modeling isn't anything new in the software developer world. It essentially comes down to hiding the complex functionality of a system (or function) and instead relying on a clear set of inputs and an expected output. Sending an email involves many network protocols and many servers handling the transaction. But, we are saved the trouble of understanding this, and instead have a textbox and a button as our input and we have our output set as a 'success'! message at the end along with our email arriving at it's desire destination.
You can send emails in the same fashion using a variety of email providers like Gmail and Yahoo and all the others out there these days. And for the most part, you do the same thing to make them all work. But they all provide this functionality in their own unique and black-boxed way. Which is what we'll be covering today.
What is black-box programming
Programming in a black-box paradigm mainly consists of having functions or methods that hides the inner workings from the user (programmer). These functions take a set number of parameters and produce the given output without the developer having to worry about how the actual logic is implemented. For example, let's say you were creating a user profile system. At some point you might need to let the users upload a profile photo, which involves saving the file locally or remotely on some server, resizing the image when appropriate, renaming the file if required, watermarking the photo, etc. Many other actions may be involved in this process.
You have to stop and ask yourself at this point "Do I need to know how the image is being cropped?". If the answer is no, then this particular function can be generalized and tucked away somewhere for safe retrieval later on. And the same would go for the watermark() and renaming() functions as well.
And this is where the black-box model comes into play. Instead of having to worry each time about how to upload files in your particular system, rewriting the same code over and over, you can have a defined set of functions that handle all of this logic for you.
function upload(file, name, directory){
}
As I mentioned, your upload function could very well call upon other blacked out functions as well, such as watermark() or crop(), or some other logic that needs to occur without us having to remember how it works. And all of these functions would work as self-sustaining entities that have no knowledge of each other.
And this is really one of the biggest benefits in black-box modeling. You can modify and improve each of these individual functions without having to notify or effect any of the other calling functions. You might find a more efficient way to crop() images or you might end up using a 3rd party solution to watermark() images at a later time for example. But in the end, you don't really want to have to worry about how these things work.
Here are a few more benefits to programming with this mindset.
Code is easier to maintain
I'd wager most websites that you use today are relatively complex in how they work. Even signing up for a website can involve a dozen or so steps, many of which have no visual elements to them, such as sending emails and verifying emails and such.The more you can modularize these functions, the easier it will be to work on the overall system. Think of it like this. If you are building an electric car, that's a pretty hefty and complex task in general. But if instead we saw it more like many smaller steps, some of which we are unaware of, such as "OrderDoors()" or "paintBlack()", jobs that really we don't have to worry too much about, then suddenly the overall task becomes much simpler.
You can feel safe that the door delivery company will do its job and that the robot that paints isn't going to fail halfway through. The same type of confidence should be felt for your code.
Scalability
Maybe you aren't processing a million files right now and so your Upload function isn't too complex or advanced at the moment, which is fine. But if everything goes as planned, at some point you might need something much more advanced and so you'll need to make a few upgrades. You might want to switch from storing files on the local server, to moving them into a cloud-based system, such as Amazon S3. Anybody using your Upload function (yourself included) should not have to worry about this inner working complexity. Again, we don't really care how we upload our files, we just want to ensure that they get where they need to get.
And now that we know why it's important, let's get into a few methods that can better help you to "black-box" your code. These are the methods that I personally normally use in my day to day coding life and they have been very beneficial during the past few years. Note that some of these methods are based on the current technology and stack that I use, and so they may or may not be beneficial to everyone. Your own programming language and stack however should offer something similar to use.
Identify what can be black-boxed
Before anything else, we have to be able to identify just what can be black-boxed. Not everything makes sense to generalize. For example, ForgotPassword() might not be modularizable. Sure, you can have a function called ForgotPassword that takes an email as an argument and does its forgot passwordy job. But can you reuse that same function for other websites? Probably not. That kind of logic belongs to the core business at hand and there would be little value in hiding its complexity.
You want to black-box complex functions that don't necessarily change very often and that can stand alone in terms of functionality. Maybe we can't black-box forgetting a password, because the the logic can vary system to system, but we can black box the logic behind sending emails. Having a function that solely handles email communications and that can be used across many projects is hugely beneficial and can save you much time. Here are a few functions that I personally have generalized:
- FileUpload()
- ReadTextFile()
- SendEmail()
- WatermarkImage()
- ResizeImage()
I can use these functions over and over in various projects without having to worry about how or if they will work. And again, these might be different for you and your type of work. As a web developer who works on many different websites, these are some of the more common utilities that I require on a day to day basis.
Optional parameters
You want to make your functions as flexible as possible and account for missing parameters. If for example, someone doesn't enter the 'directory' in your Upload function, then you'll want your black-box function to default to something that makes sense. If no name is given for a file, then maybe you'll want to auto-generate one instead. A few programming languages allow you to include optional parameters as part of the language, but for the ones that don't however, you can leverage overloading functions.
function upload(string filename, string directory, string server){
}
function upload(string filename, string directory){
}
function upload(string filename){
}
You want your function to be as robust as possible, but only if it is a realistic use case.
Naming conventions
You want your black-box functions to be intuitive without too much thought as to how they function or what they do. And as such, you want to make sure that they have a descriptive naming convention and that the corresponding parameters make sense as well. A well written function should not have to be reread over and over again until its meaning is understood.
function upload(string file, string destination)
This for example, is not a clear enough definition. You can't tell off the bat what type of formatting any of the parameters requires. Are you uploading a file url, or a string of bytes, and destination is just as ambiguous. Having full and descriptive names is key for usability.
function uploadFile(string filename, string directoryname, string fullpath, string relativepath...){
}
Essentially, you want all of your bases covered when creating generalized and modularized code. And you want to be as clear as possible so that others don't have to track down the function source in order to see how it works. Which brings me to my last point.
Centralize your code
One of the major goals in black-boxing your code is that you can reuse your functions over and over throughout your projects. In order to do so however, you'll pretty much need to keep you code in a collection somewhere. Again, this will depend on the desired technology that you are using. For my particular needs and stack, I keep all of my black-boxed code in its own separate codebase and project. This code is then compiled down to a DLL which I use on most every project. To read more about how I go about doing that, check out this post.
I'll say this, there is nothing more satisfying than having a complex task at hand, and knowing that you have the function that does just that sitting somewhere just waiting to be used. Your code will get more complex with time, that's guaranteed. And the sooner you can begin to create these chunks of self-sustaining code the smoother the overall journey will become.