Author Archives: admin

Data URI scheme

[Fuente:             http://en.wikipedia.org/wiki/Data_URI_scheme]

El esquema data URI es un URI scheme (Uniform Resource Identifier scheme) que proporciona una forma de incluir datos in-line en páginas web como si fueran recursos externos. Esta técnica te permite por ejemplo descargar en una sola petición HTTP imágenes y hojas de estilos antes que hacer múltiples peticiones HTTP lo cual puede ser ineficiente.

Los data URIs tienden a ser más simples que métodos de inclusión, tales como MIME with cid or mid URIs. Los data URIs se les llama algunas veces URL (Uniform Resource Locators) , aunque no referencien nada remoto. El esquema data URI está definido en RFC 2397 of the Internet Engineering Task Force (IETF).

En los browsers que soportan completamente Data URIs para “navigation”, contenido generado por Javascript se le puede provocar como si fuera una descarga de fichero para el usuario, simplemente asignando el Data Uri al window.location.href. Como ejemplo valga la conversión de tablas HTML a CSV para descargar utilizando Data un URI como este:

data:text/csv;charset=UTF-8,' + encodeURIComponent(csv)

donde “csv” ha sido generado por Javascript.

La IETF publicó la especificación data URI en 1998 y no ha cambiado desde entonces. La especificación HTML 4.01 se refiere al data URI scheme y este esquema ha sido implementado en la mayoría de los navegadores.

Web browser support

As of March 2012, Data URIs are supported by the following web browsers:

  • Gecko-based, such as FirefoxSeaMonkeyXeroBankCaminoFennec and K-Meleon
  • Konqueror, via KDE‘s KIO slaves input/output system
  • Opera (including devices such as the Nintendo DSi or Wii)
  • WebKit-based, such as Safari (including iOS), Android‘s browser, Kindle 4’s browser, Epiphany and Midori (WebKit is a derivative of Konqueror’s KHTML engine, but Mac OS X does not share the KIO architecture so the implementations are different), and Webkit/Chromium-based, such as Chrome
  • Trident
    • Internet Explorer 8: Microsoft has limited its support to certain “non-navigable” content for security reasons, including concerns that JavaScript embedded in a data URI may not be interpretable by script filters such as those used by web-based email clients. Data URIs must be smaller than 32 KB in Version 8.[3] Data URIs are supported only for the following elements and/or attributes:[4]
      • object (images only)
      • img
      • input type=image
      • link
      • CSS declarations that accept a URL, such as background-imagebackgroundlist-style-typelist-style and similar.
    • Internet Explorer 9: Internet Explorer 9 does not have 32KB limitation and supports more elements.

Email Client support

Following clients support data URI for images[3]

Format

data:[<MIME-type>][;charset=<encoding>][;base64],<data>

The encoding is indicated by ;base64. If it’s present the data is encoded as base64. Without it the data (as a sequence of octets) is represented using ASCII encoding for octets inside the range of safe URL characters and using the standard %xx hex encoding of URLs for octets outside that range. If <MIME-type> is omitted, it defaults to text/plain;charset=US-ASCII. (As a shorthand, the type can be omitted but the charset parameter supplied.)

Some browsers (Chrome, Opera, Safari, Firefox) accept a non-standard ordering if both ;base64 and ;charset are supplied, while Internet Explorer requires that the charset’s specification must precede the base64 token.

Advantages and disadvantages

Advantages

  • HTTP request and header traffic is not required for embedded data, so data URIs consume less bandwidth whenever the overhead of encoding the inline content as a data URI is smaller than the HTTP overhead. For example, the required base64 encoding for an image 600 bytes long would be 800 bytes, so if an HTTP request required more than 200 bytes of overhead, the data URI would be more efficient.
  • For transferring many small files (less than a few kilobytes each), this can be fasterTCP transfers tend to start slowly. If each file requires a new TCP connection, the transfer speed is limited by the round-trip time rather than the available bandwidth. Using HTTP keep-alive improves the situation, but may not entirely alleviate the bottleneck.
  • While web browsers will not cache inline-loaded data as separate resource, external CSS files using data URIs are cached, so that an external CSS file with 10 background-images embedded as data URIs requires only one initial request instead of eleven and subsequent requests require only retrieving one cached CSS file, instead of one CSS file plus ten cached images.
  • When browsing a secure HTTPS web site, web browsers commonly require that all elements of a web page be downloaded over secure connections, or the user will be notified of reduced security due to a mixture of secure and insecure elements. On badly configured servers, HTTPS requests have significant overhead over common HTTP requests, so embedding data in data URIs may improve speed in this case.
  • Web browsers are usually configured to make only a certain number of (often two) concurrent HTTP connections to a domain,[5] so inline data frees up a download connection for other content.
  • Environments with limited or restricted access to external resources may embed content when it is disallowed or impractical to reference it externally. For example, an advanced HTML editing field could accept a pasted or inserted image and convert it to a data URI to hide the complexity of external resources from the user. Alternatively, a browser can convert (encode) image based data from the clipboard to a data URI and paste it in a HTML editing field. Mozilla Firefox 4 supports this functionality.
  • It is possible to manage a multimedia page as a single file.
  • Email message templates can contain images (for backgrounds or signatures) without the image appearing to be an “attachment”.

Disadvantages[edit]

  • Data URIs are not separately cached from their containing documents (e.g. CSS or HTML files), therefore the encoded data is downloaded every time the containing documents are re-downloaded.
  • Content must be re-encoded and re-embedded every time a change is made.
  • Internet Explorer through version 7 (approximately 5% of web traffic as of September 2011), lacks support. However this can be overcome by serving browser-specific content.[6]
  • Internet Explorer 8 limits data URIs to a maximum length of 32 KB. (Internet Explorer 9 does not have this limitation)[3][4]
  • In IE 8 and 9, data URIs can only be used for images, but not for navigation or JavaScript generated file downloads.[7]
  • Data URIs are included as a simple stream, and many processing environments (such as web browsers) may not support using containers (such as multipart/alternative or message/rfc822) to provide greater complexity such as metadatadata compression, or content negotiation.
  • Base64-encoded data URIs are 1/3 times larger in size than their binary equivalent. (However, this overhead is reduced to 2–3% if the HTTP server compresses the response using gzip)[8]
  • Data URIs do not carry a file name as a normal linked file would. When saving, a default file name for the specified MIME type is generally used.
  • Referencing the same resource (such as an embedded small image) more than once from the same document results in multiple copies of the embedded resource. In comparison, an external resource can be referenced arbitrarily many times, yet downloaded and decoded only once.
  • Data URIs make it more difficult for security software to filter content.[9]

Examples

HTML

An HTML fragment embedding a picture of small red dot: Red-dot-5px.png

<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA
AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO
9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot" />

As demonstrated above, data URIs encoded with base64 may contain whitespace for readability.

CSS

A CSS rule that includes a background image:

ul.checklist li.complete {
    padding-left: 20px;
    background: white url('data:image/png;base64,iVBORw0KGgoAA
AANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0l
EQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6
P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC') no-repeat scroll left top;
}

In Mozilla Firefox 5Google Chrome 17, and IE 9 (released June, 2011), encoded data must not contain newlines.

JavaScript

JavaScript statement that opens an embedded subwindow, as for a footnote link:

window.open('data:text/html;charset=utf-8,' + 
    encodeURIComponent( // Escape for URL formatting
        '<!DOCTYPE html>'+
        '<html lang="en">'+
        '<head><title>Embedded Window</title></head>'+
        '<body><h1>42</h1></body>'+
        '</html>'
    )
);

This example does not work with Internet Explorer 8 due to its security restrictions that prevent navigable file types from being used.[4]

How to Create a Simple WordPress Plugin

[Fuente: http://wpninjas.com/how-to-create-a-simple-wordpress-plugin/]

When giving support or offering various snippets we frequently advise people to create a WordPress plugin to handle all of their modifications. Here is how you create a very simple plugin to house all your little snippets and modifications.

Basic Structure

A WordPress plugin can consist of as little as a single PHP file however for consistency I always create folder and at least the single PHP file within it. The folder and the file should have the same name with the exception of the file extension. We’re going to call our plugin “My Custom Functions” so the directory and file would be named accordingly.

  • Folder: my-custom-functions
  • File: my-custom-functions.php

When we’re done we’re going to upload this newly created folder with the file inside it to our wp-content/plugins directory on our server.

Basics of a WordPress Plugin file

There are no doubt endless conversations that could be had about all the things that can go into your plugin file but I’m going to cover the absolute necessities to get you started quickly.

WordPress Plugin Header

Here is all that needs to be in your plugin file for WordPress to recognize it once uploaded.

1
2
3
4
<?php
/*
Plugin Name: My Custom Functions
*/

See how easy that is? At this point you have a plugin that you can activate in your WordPress plugins area. Of course our plugin doesn’t actually do anything yet but the point is you have laid the foundation of your very own plugin and it was super easy.

Now there are other elements that you can include in this header. Things like a description, version, author, etc. You can read more about those here: http://codex.wordpress.org /Writing_a_Plugin #File_Headers

The rest of your plugin

As I said earlier there is really no end to what you can place inside your plugin but at this point, at a very basic level, you can think of it like your themes functions.php file. By that I mean that if you did nothing else you could place all those little code snippets that you love so much into this file instead of in your functions.php file and they would work exactly the same way.

As an example consider this little snippet that I sometimes use when I want to very quickly be able to redirect a page completely to a different page whether on the same site or another site entirely.

1
2
3
4
5
6
7
8
9
10
function my_custom_redirect () {
    global $post;
    if ( is_page() || is_object( $post ) ) {
        if ( $redirect = get_post_meta($post->ID, ‘redirect’, true ) ) {
                        wp_redirect( $redirect );
                        exit;
                }
        }
}
add_action( ‘get_header’, ‘my_custom_redirect’ );

This snippet is very glamorous but what it essentially does is allow me to add a piece of custom post meta to any page called “redirect” with a URL. When the page is called and has this custom post meta then it’s automatically redirected to the new URL. Let take a look at it altogether now.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
/*
Plugin Name: My Custom Functions
*/
function my_custom_redirect () {
    global $post;
    if ( is_page() || is_object( $post ) ) {
        if ( $redirect = get_post_meta($post-&gt;ID, ‘redirect’, true ) ) {
                        wp_redirect( $redirect );
                        exit;
                }
        }
}
add_action( ‘get_header’, ‘my_custom_redirect’ );

As you can see this plugin is not complicated at all and the best part is if something seems to be going fishy with my redirects I don’t have to dig through a huge file of miscellaneous functions to find it. In fact I could have named this plugin My Custom Redirect and then if anything happened I could just deactivate this one plugin without having an adverse affect on my entire site.

 In Summary

You can no longer say that it’s to difficult to create a plugin. It’s just as easy as adding code to your functions.php, which you should almost never do, and so much cooler too.

Bonus Round

If you are interested in digging into creating WordPress plugins more complicated than what I describe above consider checking out the following resources.

Enterprise Integration Using REST

[Fuente: http://martinfowler.com/articles/enterpriseREST.html]

La mayoría de los REST APIs son construidos pensando en un sólo propósito para integrarse en un solo sistema. En este artículo , discutiremos las restricciones y flexibilidad que tienes con APIs no públicos, y las lecciones aprendidas de hacer integraciones RESTful a gran escala que utilizan varios equipos de trabajo.

18 November 2013


¿Por qué se utiliza REST en los sistemas corporativos?

Hacer cambios en un sistema heredado de gran tamaño  es muy duro dentro del a industria IT.

Muchas veces nos metemos en ese tipo de cambios imaginando que la inmaculada arquitectura que el nuevo sistema tendrá y cometemos el error de subestimar la dificultad de la tarea. Ciertos aspectos salen a la luz , aspectos que no son entendibles cuando son vistos contra el telón de fondo de un sistema heredado completo , hecho a medida y masivamente enredado. Lo primero que se intenta es comprar un paquete software con el objetivo de reducir el esfuerzo de integración con la promesa de no estar atado nunca más a un sistema que no tiene soporte.Como segunda opción se propone realizar una arquitectura orientada para integración, con el objetivo de reemplazar sólo algunas partes del nuevo sistema y reducir asi el esfuerzo de cambiar todo el proyecto.

ThoughtWorks ha estado involucrado en varios grandes proyectos que heredan sistemas. REST sobre HTTP es una opción muy apetecible para muchos proyectos y es por la que apostamos normalmente. Es simple de utilizar y de entender y no requiere ningún framework raro para empezar a trabajar con él. Arquitecturamente hablando, REST está probado que tiene buena escalabilidad y encaja bien con modelado de dominios.


Define entornos lógicos – uno por cada necesidad

Many large IT organizations inherit a legacy of expensive environments from mainframes or large vendor installations and try to shoe-horn services into a predefined list of inflexible environments. Unfortunately, managing an enterprise-wide set of environments that all developers must use gives up one of the principal advantages of RESTful services: lightweight environments. While services may be a facade in front of applications that require substantial horsepower, the services themselves tend to be simple to deploy and host, and testable through a browser and a command line. Furthermore, techniques such as using ATOM-like event feeds avoid the need for extensive middleware infrastructure in spinning up a new environment. The key insight is to understand the concept of a logical environment:

logical environment is an appropriately isolated set of interrelated applications, services, and infrastructural components needed to satisfy a business or development need.

The components needed to satisfy a development need may be quite different for the various teams and roles than the components needed to satisfy a business need. Few developers in large organizations expect to run an isolated full-stack environment, and isolation should go only as far as needed to make developers productive. For example, in a retail project, a developer in the order entry team may require services for the product catalog and customer management, but perhaps not for warehouse management. In production, each of those may have a load-balanced cluster supporting them, but developers and QAs value isolation over performance and availability. In the extreme case, different developers may have different logical environments on the same VM. In this case, the isolation can be accomplished by making ports and database names part of the environment configuration.

Figure 1

Figure 1: Environmental isolation is independent of the hardware hosting the environment

The other problem with shared environments is that everybody gets upgraded at the same time, which is often not appropriate in the chaotic world of development. Much better is to put the release schedule in the hands of the individuals affected by it – this is equally true for production releases as it is for developers upgrading a service they depend on in their sandbox environment. This can be particularly important for QAs, a role that has a stong need for managing the release cadence within their logical environment. Testing requires a known and stable set of versions for the services involved, and developers find fixing bugs considerably easier when the version is known.

In one large engagement, we defined a declarative description of environments using Yaml. A top level key would define an environment name, sub-keys defined the required services for that environment, and the next level of keys defined environment-specific configuration for each service:

order-entry-dev:
  product:
    webservers: [localhost]
    port: 8080
    logPath: /var/log/product
    dbserver: localhost
    dbname: product
  customer:
    webservers: [localhost]
    port: 9080
    logPath: /var/log/customer
    dbserver: localhost
    dbname: customer

Ruthless attention to deployment automation and appropriate investment in infrastructure meant that some services existed in over 50 logical environments, a mind-blowing number for a company accustomed to mainframes. Tools like Ansible help declaratively describe environments without a heavy up-front investment. To allow for the kind of lightweight ad-hoc environments that developers use, it’s often helpful to define a single environment withlocalhost as the server name, which can be spun up on a local virtual machine using something like Vagrant. This allows environmental elasticity by using the same environment configuration but different VMs.

What about packages?

Vendor packages complicate environment creation, as they are rarely built to support easy deployment and environmental elasticity. Many of them ship with an installation document that changes with every upgrade and no reliable mechanism to replay changes in multiple environments. Licensing also adds a hurdle, although most vendors will allow low cost development licenses.

If you find yourself burdened with a vendor package that is hard to deploy, there are a couple of remediation strategies. If the package does not require complicated licensing during installation, you may be able to do the vendor’s work of automating the installation and upgrade. Alternatively you can set up a cloneable VM, which gives you elasticity but complicates upgrades. Essentially, this is the bake vs. fry distinction in configuration management discussions.

When neither option is available, there are other ways of achieving some level of isolation, although none will be comparable to actual environmental isolation. There may be a way of using natural data boundaries within the application to allow some measure of developer isolation. Different user accounts tend to be an easy data boundary, although users tend to share global state. Better still is to provide different tenants to individual developers, as multi-tenant applications are designed to prevent cross-tenant traffic. This approach is clearly a work-around, has scaling challenges, and does not provide release scheduling independence.

Ease of deployment and environment management should be one of the criteria by which packages are selected.

The best solution, of course, is to vet such operational considerations during the vendor selection process. Ease of deployment and environment management should be one of the criteria by which packages are selected. During the selection process, we have to consider not only feature set and fit-to-purpose, but ease of integration and the productivity of the integration developers.


Use versioning only as a last resort

An important corollary to the definition of a logical environment is the notion of cohesion – each environment should have only one instance of a given service. Unfortunately, in large projects where each team moves at a different pace, it is all too easy to run into the classic diamond dependency problem usually associated with compile time dependencies:

Figure 2

Figure 2: Incompatible version requirements

In my experience, one of the first solutions RESTful architects reach for is versioning. I take a more controversial view. To borrow Jamie Zawinski’s famous dig on regular expressions:

Some people, when confronted with a problem, think “I know, I’ll use versioning.” Now they have 2.1.0 problems.

The problem is that versioning can significantly complicate understanding, testing, and troubleshooting a system. As soon as you have multiple incompatible versions of the same service used by multiple consumers, developers have to maintain bug fixes in all supported versions. If they are maintained in a single codebase, developers risk breaking an old version by adding a new feature to a new version simply because of shared code pathways. If the versions are independently deployed, the operational footprint becomes more complex to monitor and support. This extra complexity is either overlooked or justified by simplifying the release process of interdependent services. However, the release complexity can be mitigated significantly with a disciplined use of consumer-based testing (discussed in the next section), an intriguing option available to enterprise APIs that is not available to public APIs.

For many types of changes, versioning can be avoided by other techniques. Postel’s Lawstates that you should be liberal in what you accept and conservative in what you send. This is sage advice for service development. Unfortunately, the default behavior of some deserializers breaks this advice by throwing an exception when an unexpected field is provided by a consumer. This is unfortunate, as it could simply be the consumer passing additional diagnostics over the wire with no expectation of consumption, or it could be the consumer preparing for a future update of the producer, passing a new field before the producer is prepared to deal with it. It could be the producer adding a new field to a response body which the consumer is free to ignore. None of these situation warrants an exception.

Automatic deserialization usually falls into the pitfall of coupling consumers and producers.

It’s usually possible to configure the deserializer to be more tolerant. Though it’s not mainstream advice, I prefer to avoid automatic deserialization altogether. Automatic deserialization usually falls into the WSDL pitfall of coupling consumers and producers by duplicating a static class structure in both. Hand-coded deserialization also allows for fewer assumptions to be made in the incoming data. As Martin Fowler describes in Tolerant Reader, using XPath expressions like //order allows nesting changes above the order element without breaking deserialization.

When it comes to service contract design, a little up-front design can pay big benefits. In one project, a contract had been developed with inconsistent casing of the attributes – firstNameand LastName for example. Developers on consumer teams no doubt swore under their breath when they developed against the contract, but they swore quite loudly when the contract was subsequently “fixed” without notice.

In large SOA projects, I prefer writing many stories at service boundaries. This does lead to the obvious challenge of making sure the end-to-end functionality aligns with business goals (a problem I discuss later), but has many advantages. First, they naturally tend to involve the tech lead or architect in the analysis, giving them time to think about the granularity of the concepts and mock out the contract to form a cohesive description of the resource. Writing the acceptance criteria requires thinking through the various error conditions and response codes. Having QA review at service boundaries gives another opportunity to catch the obvious mistakes, like the casing issue just mentioned. Finally, in much the same way that test-driven development encourages loose coupling by making sure each class has at least two consumers – the consumer it was written for and the tests – service boundary stories help ensure that the service endpoint is reusable rather than overly specific to the end-to-end functionality it is initially developed for. Being able to test a service endpoint in isolation prevents coupling it too tightly to the end-to-end workflow it supports.

Producers can also signal when they need to make a breaking change using semantic versioning. This is a simple scheme to add well known meanings to the MAJOR.MINOR.PATCH portions of a version, including incrementing the MAJOR version for breaking changes. If you have a disciplined set of consumer-driven tests (described shortly), you may be able to upgrade all consumers on the same release. Of course, that isn’t always possible, and at some point the complexity of supporting multiple versions of the same service may be justified because coordinating a release of its dependencies is even more complex. Once you reach the point where you have to use versioning, there two principal techniques to choose between: URL versioning and HTTP header versioning. In your choice it is important to understand that the versioning scheme you select is first and foremost a release management strategy.

URL versioning involves including a version number in the URL (such as /customers/v1/… – the MAJOR version in semantic versioning is sufficient). For this the consumer will have to wait until the producer has been released. URL versioning has the advantage of being very visible, and testable through a browser. Nevertheless, URL versioning suffers an important flaw when one service provides links to another service with the expectation that the consumer will follow the link (this is most common when trying to use hypermedia to drive workflow). If the hyperlinked service upgrades to a new version, coordinating upgrades across such dependencies can get tricky. For example, the customer service links to the product service, and the UI follows that link blindly, unaware of product versioning since the version is embedded in the provided link. When we make a non-backwards compatible upgrade to the product service, we ultimately want to upgrade the customer service as well to update the link, but we have to be careful to first upgrade the UI. In this situation, the simplest solution is often to upgrade all three components at the same time, which is effectively the same release management strategy as not versioning at all.

Duncan Beaumont Cragg suggests simply extending the URL space rather than versioning it. When you need to make an incompatible change, simply create a new resource rather than versioning the existing one. On the surface, there is a small change between/customers/v2/profile and /customers/extendedProfile. They may even be implemented the same way. However, from a communication standpoint, there is a world of difference between the two options. Versioning is a much broader topic, and in large organizations, versioning can often require coordination with multiple outside teams, including architecture and release management, whereas teams tend to have autonomy to add new resources.

HTTP header versioning puts information into the HTTP header indicating which version the consumer will accept. This is most commonly associated with the Content-Type, for example, application/vnd.acme.customer-v1+json, which allows content negotiation to manage the version. A client can send a list of supported versions in the Accept header, and the server can respond with the version used in the Content-Type header, or send a 415 HTTP status code for an unsupported version request. This appeals to purist RESTafarians, and is immune to the flaw mentioned above with URL versioning, as the ultimate consumer gets to decide which version to request. Of course, it becomes harder to test through a browser and easier to overlook when developing. It’s helpful to augment header versioning by also putting the version number in request and response bodies, when present, to provide additional visibility. Header versioning also introduces challenges with caching. The Varyheader was designed to enable the same URL to be cached in different ways, but it adds complexity to your network configuration, and you risk running into a misconfigured network cache that ignores the Vary header.


Catch integration problems with consumer-based testing

Consumer-based testing is one of the most valuable practices I’ve seen that makes REST scale within an enterprise, but before we dive in, we need to understand the concept of a deployment pipeline.

Deployment Pipelines

In their groundbreaking book on Continuous Delivery, Jez Humble and Dave Farley portray the deployment pipeline as the path code takes from checkin to production. If we follow a checkin to a production release in a large organization, we might find the following steps:

  • A developer checks in new code.
  • The continuous integration tool compiles, packages, and runs unit tests against the source code (often called the commit stage).
  • The continuous integration tool deploys to a sandbox environment to run a set of automated tests against the deployed service in isolation.
  • The application team deploys to a showcase environment where internal user acceptance occurs by the business stakeholder.
  • The central QA team deploys to a systems integration test (SIT) environment, where they test alongside other applications and services.
  • Release management deploys into pre-production, where the application team, security, and operations perform some manual validation of the quality of the release.
  • Release management deploys into production.

Modeling that workflow as a series of stages gives us our deployment pipeline, which lets us visualize the version of our service in each of the pipeline stages:

Figure 3

Figure 3: Simple deployment pipeline

The pipeline depicted above describes the flow for a single service in isolation. The reality of large-scale SOA projects is considerably more complicated as we add integration:

Figure 4

Figure 4: Integrated deployment pipeline

Note that in the integrated pipeline, I’ve left out a lot of detail in the early stages. Different teams often have different stages for the team-facing portions of the pipeline, which may be isolated from true external dependencies. Some teams, for example, may add manual QA or performance test stages. At the organizational stages – SIT, pre-prod, and production – it is fairly common for all code to progress the same way, and for those stages to test the services integrated together. The further you go down the pipeline, the more integrated it is and the more production-like are the environments.

An investment in stubbing can pay off large dividends in the early stages of a pipeline. It’s comforting to know that when your build goes red it’s because of broken code within your team and not a change in the environment you happen to be testing in. Using a test double helps eradicate a leading cause of non-determinism in your tests. Libraries like VCR allow you to record real service calls and replay the responses later during automated test runs. Using test doubles does leave you exposed to integration problems, though, and most of the complexity in enterprises involves integration. Fortunately, Ian Robinson describes a solution to tangling the integration complexity, which fits in well with our deployment pipeline.

Consumer-based testing

Consumer-based testing is counter-intuitive, as it relies on the consumers writing tests for the producer. When writing contract tests, a consumer writes a test against a service it uses to confirm that the service contract satisfies the consumer’s needs. For example, the order entry team may depend on the code and description of the product service, and that the monthly charge is a number, and so they write a test like this:

[Test]
public void ValidateProductAttributes()
{
    var url = UrlForTestProduct();
    var response = new HttpResource(url)
                        .ThatAccepts("application/xml")
                        .Get();

    Assert.That(response.StatusCode, Is.EqualTo(200));
    AssertHasXPath(response.Body, "//productCode");
    AssertHasXPath(response.Body, "//description");
    AssertHasXPath(response.Body, "//monthlyCharge");
    AssertNumeric(ValueFor(response.Body, "//monthlyCharge"));
}

This enables a neat trick in the deployment pipeline. After an individual service passes its internal pipeline, all services and consumers go through a unified contract test stage. It could be triggered by the consumer changing tests, or the producer committing changes to the service. Each consumer runs its tests against the new version of the changed service, and any failure prevents the new version from progressing in the pipeline.

In our example, let’s say a new build of the order entry service progresses to the contract test stage:

Figure 5

Figure 5: The contract test stage

It depends on the product and billing services, and since the new code could have changed its contract tests, it runs its tests against the last version of the product and billing services to make it into the contract test stage. The UI depends on the order entry service, so the last version of the UI code in the contract test stage runs its tests against order entry. This means that both the services and their consumer tests are required in the contract test stage. Since the product service has no dependencies, it has no consumer tests. Let’s look at our diamond again; this time note that there is only one version of each service depended on.

Figure 6

Figure 6: Sample contract test run

Triggering only the tests associated with a particular change can get tricky, but you can go a long way simply by running all contract tests each time a new service is deployed to the contract test stage of the pipeline. This would include the grey lines in Figure 6, which aren’t relevant to the change that was introduced. It is a tradeoff between speed of the test run and how complex you’re willing to make the dependency management.

Assuming all the tests pass, we now have a set of services that have been shown to work together. We can record the set of them together, and their associated versions.

Figure 7

Figure 7: Successful contract test run

At this point, all versions of the services involved are captured in a single deployable artifact set, or DAS. This DAS can become the single deployable artifact for higher stages of the deployment pipeline. Alternatively, it can provide a compatibility reference if supporting independent releases is required. Either way, it represents a set of components that have been proven to speak the same language.

If the new order entry code broke the UI consumer tests, the combined artifact does not progress. This doesn’t necessarily indicate a problem in the service; it could be a misunderstanding of the contract by the consumer. Or, it could be an intentional breaking change from the producer, although according to semantic versioning etiquette, they should have incremented their MAJOR number if it was intentional. In any case, failing contract tests triggers a conversation between the two teams early, rather than days or weeks later when the consumer updates their environment.

Figure 8

Figure 8: Breaking contract change

What about data?

One of the harder challenges with getting comprehensive consumer-based testing is generating valuable test data. The contract test above assumes a known test product. I hid this assumption away with a UrlForTestProduct method, which presumably requires having a URL for a specific product. Hard-coding assumptions about data available at the time of the test can be a fragile approach, as there are no guarantees that product will continue to exist in the future. Furthermore, some endpoints may require data consistency across multiple services to work. For instance, order entry may submit an order to billing with a set of associated products. The billing endpoint will need to have a consistent set of product data.

One of the more robust strategies is to create the test data during the test run, as you guarantee the data exists before using it. This presupposes that each service allows creating new resources, which isn’t always the case, although at one client we added test-only endpoints to facilitate test data creation. These endpoints were not exposed in production. This strategy can still require complicated test setup in the billing example above. After creating a test product, you would have to force synchronization with the order entry and billing services, an operation that is often asynchronous in nature. An alternate strategy is to have each service publish a cohesive set of golden test data that it guarantees to be stable. This is usually best encapsulated in some higher level data boundary. For example, you may create a test marketing brand or line of business that crosses all the services for the sole purpose of providing fake data that won’t have production impacts. Either way, wrangling test data should be a first class concern for enabling a robust service deployment pipeline.


Do not let systems monopolize resources

Defining data boundaries poorly is one of the most expensive mistakes an architect can make. A common anti-pattern is to attempt to store all information about an entity in a single data store, and export it to dependent systems as needed, a strategy encouraged by a superficial misunderstanding of master data management (MDM). The problem with such a strategy is that it violates Conway’s Law, which states that software architectures are bound to reflect the structure of the organization that built them [1].

Let’s look at an example of a product catalog. In the legacy system, one team entered new product codes and their associated rates. A provisioning team used another tool to enter appropriate configuration, such as the codes downstream phone provisioning systems needed, and the service codes to turn on TV channels in another application. The finance team entered general ledger information about the product in their financial tool, and the invoicing team added special invoicing rules in yet a different application. The communication between these teams was managed by the business, not by technology.

Transitioning to a single application for all product data can be a disastrous exercise, primarily because those different business teams all have a different definition of what a product is. What a customer service representative thinks of as a single product may have to split in two to support proper accounting. The invoicing team, highly concerned with reducing call rates by simplifying the invoice, often needs to merge two product codes into a single line on the bill. And of course there’s always marketing, who redefines products with reckless abandon. Attempting to rationalize the entire enterprise view of a product into a single catalog simply makes that catalog both fragile and inflexible. Effectively, the entire enterprise must now descend into the product catalog to make a change. The surface area of change is significantly increased, and the ripple effects from a change become hard to reason about. Conway’s Law is violated, as the communication paths of the system no longer represent the communication paths of the organization.

Figure 9

Figure 9: A data modeling disaster

I am more than a little skeptical of universal data models that try to standardize the canonical representation of something as important as a product across an enterprise and its integration partners. The telecommunications industry made just such a data model, called the TM Forum Shared Information/Data Model (TMF SID). The claim is that, by standardizing on the SID, different companies, or departments within a company, can use the same terms to describe the same logical entities. I suspect such a massive undertaking would not be sustained without some successes, but I’ve not seen them.

My recommended solution, borrowing from Eric Evans’ Domain Driven Design , is to wrap each team’s definition of a product behind a bounded context. A bounded context is a linguistic boundary, within which a term is guaranteed to mean the same thing wherever it is used. Outside the bounded context, no such guarantees exist, and a combination of integration and business process must manage the translation. That a financial product is different from a provisionable product can now be modeled in a way that abides by Conway’s Law.

Providing well-defined bounded contexts around packages is a great use of facade services. A natural consequence of vendors evolving their package to support multiple businesses is that the feature set of the package likely extends beyond your enterprise needs. Wrapping all communication with the package behind a facade service allows you to limit and shape the package data into the bounded context that your business process defines, and provide a clean API that transforms the package-specific vernacular into the language defined by your enterprise.

Use a small subset of data for centralized entities

The way we do this technically is to shrink the set of master data that resides in the product catalog, and replicate that data to other systems, where it is augmented. This technique is sometimes associated with “swivel chair integration,” but it’s not the same. In true swivel chair integration, the same information is entered in multiple applications that do not directly integrate. With bounded contexts, master data is replicated, and is contextually augmented and transformed in dependent applications.

The key to defining a central resource entity like a product is to understand what the business team that creates new products thinks a product is. In our hypothetical example, they decide that a product defines some basic rates and common descriptive information used by all departments. That gives us our definition of a product in the product service: a product is something we can sell to a customer that has a single rate. In the legacy world, after creating a product, the product team sends out an email to other departments. This event is likely worth automating, which we can do by publishing on a queue or a change feed exposed on our service. However, we should not pretend that we can automate away the business process that this event triggers, nor should we move that business process inside the central catalog.

When finance receives it, they have to decide how to decompose the product. Let’s say it was a bundled package of TV sports channels at a discounted price, a concept that neatly fits the product teams definition of a product. However, finance needs to make sure that each sports station within the bundle gets their royalty fees, and so they need to send the single rate to different general ledger buckets. Now we have a financial definition: a product is a association of revenue to a general ledger account. In the use case just described, we can imagine the finance application receiving the NewProduct event, and providing an interface for the user to assign portions of the revenue to ledger accounts.

Each business unit has a different model for common entities with explicit translation between their bounded contexts

When billing receives the event, they need to decide whether or not to prorate the package. Perhaps, concerned with too many customers ordering a prorated sports channel only to cancel the next day after watching the Big Game, they decide that this particular sports package requires paying for a full month up front. We start with the definition that a product is a recurring charge to a customer, and augment with a set of potentially complex configuration beyond the simple monthly rate.

Invoicing defines a product as a line on a bill. They may decide, perhaps driven by a marketing strategy, that customers who purchased two separate sports bundles should see only one line on the bill called “Super Sports Package,” with the summed amount. Again, we can imagine an application facilitating receiving the NewProduct event and allowing such combining, or we can imagine developers coding new rules upon such product introductions.

Figure 10

Figure 10: Example integration using bounded contexts

This example shows four different bounded contexts, and the NewProduct event being propagated using a notification feed, which is a common RESTful approach. When a new product is created, the product service records the new product as an event which it exposes on an endpoint. Consumers poll the endpoint to receive all events since the last time they polled at an endpoint that may look like /notifications?since=2013-10-01.


Use epics to coordinate business features

Earlier, I recommended considering service endpoints as story boundaries, with the caveat that we may lose the benefit of traditional agile stories – namely, that they are aligned with business functionality. Since teams work with different priorities and at different speeds, this problem is exacerbated in large scale SOA, with the risk that we lose the forest for the trees. Imagine a business feature to bill for the first month of a product. A true business flow may require a series of service calls prior to that point, involving customer creation, product lookup, order creation, and field technician approval. At scale, those service endpoints will be implemented by different teams.

The agile toolbox has always included epics for coordinating groups of stories along a single high level feature. I recommend treating them as first class citizens of program management for large scale SOA. In fact, I believe much of the ceremony we attach user stories deserves to be at the epic level for such projects, since the epics often stand in for our business friendly accounts of requirements.

For example, a Create Customer epic may involve the order entry and billing teams in addition to the customer management team, each of which have individual service-oriented or application-specific stories for tracking their work. In our hypothetical example where the services are used by a user interface developed by a separate team, we may not be able to showcase the fruits of our labor to the business until we’ve completed the full system flow.

Some of the care and management of epics can be lifted from story practices on smaller engagements. Getting architectural review of an epic to define cross-functional requirements, and business analysts to define acceptance requirements can help keep the whole picture in mind. Cross-team showcases should be managed at the epic level, and these may be the first showcases where the actual business user flow is shown.

Program-level metrics keep epics as the principal metric for tracking velocity, as team user-story velocity can give a false sense of progress.

The important consideration is that program-level metrics keep epics as the principal metric for tracking velocity, as team user-story velocity can give a false sense of progress. The symptom to watch for is when velocity burnups show the program delivering on time, but nothing seems to work. I was on one project where this was the case based on individual story velocity tracked by the individual teams. Some teams were on time, others slightly behind schedule, but we were unable to showcase anything to the business after months of development. Simply changing the program-level burnups to show the number of epics completed instead of the number of stories completed was illuminating. Despite individual teams showing significant progress, we had only completed one epic. Worse yet, at least one story from a full two-thirds of all epics needed to release was in play at the same time. Traditional software kanban approaches attempt to limit work in progress at the story level. When we realized the scope of the problem, we were able to course correct by resequencing the story development to limit the number of epics in progress at the same time.


Wrapping up

Regardless of the technology or architecture, scaling software development is tricky business. We often fool ourselves into thinking otherwise by pretending that it’s “just integration.” Eric Evans’ once said that, in any large system, some of it will be poorly designed. My experience – even with highly skilled teams – has led me to believe that he’s right. Our principal goal of integration, therefore, is to ensure that we insulate ourselves from another subsystem’s design.

I am an advocate for a RESTful service integration strategy. I believe REST makes for simpler development and, because RESTful messages tend to be self-descriptive, simpler testing and troubleshooting. However, it is far from the silver bullet some imagine it to be, and RESTful integration at scale requires paying attention to the lessons described above. My own experience has led me to believe that:

  • Environmental isolation matters. Paying attention to deployment automation is critical. Selecting packages that disregard good deployment practices will slow everyone who needs to integrate with that package down.
  • Versioning prematurely adds unnecessary complexity to the system. Practices such as tolerant deserialization and endpoint-based story analysis help you delay versioning, and are useful practices even if you subsequently add semantic versioning support.
  • Using consumer tests greatly reduces the release management complexity of upgrading a set of inter-dependent services, and further helps delay versioning.
  • Attempting to let a service control all data about an entity is disastrous. Do not ignore business process and the different definitions the business has for an entity.
  • Coordinating business functionality is unlikely to happen at the user story level at scale. Epics are required to sequence business releases.

Using hypermedia, content negotiation, and a uniform interface get all the attention in RESTful discussions, and they are valuable techniques, but making integration solutions scale requires us to step away from the mechanics of REST and look at social and organizational issues. Navigating such issues successfully does not mean that each individual service, or component, will be well designed. It does mean that you will have solid practices in place to incrementally deliver business value and ensure appropriate robustness at the integration layer, and those practices may spell the difference between a successful delivery and a failed one.


For articles on similar topics…

…take a look at the following tags:

application integration web servicesenterprise architecture


Acknowledgments

Special thanks to Martin Fowler, Damien Del Russo, Danilo Sato, Duncan Beaumont Cragg, and Jennifer Smith for early feedback on this article. Many of the ideas came from using my ThoughtWorks colleagues as sounding boards on different projects. While there are too many to name here, I would like to call out Ryan Murray, Mike Mason, and Manoj Mahalingam for their particular influence on some of the ideas presented here.

Footnotes

1: Conways’ Law

There are several different viewpoints on Conway’s Law, and some see it as a purely descriptive tautology. I’m using it more in line with the variation that Wikipedia associates with James Coplien and Neil Harrison. It may indeed be a descriptive law of software within an organization, but I believe that’s only because software that attempts to work against Conway’s Law is doomed to fail.

Significant Revisions

18 November 2013: added option of extending the URL space to avoid versioning, and a footnote to clarify Conway’s Law usage

12 November 2013: added section on using epics, thus completed initial publication

08 November 2013: added section against letting systems monopolize resources

31 October 2013: added section on consumer-based testing

24 October 2013: added section on versioning

21 October 2013: release first edition with logical environments section

Como un buen whisky escoces, los programadores mejoran con la edad

[Fuente: http://www.wired.com/wiredenterprise/2013/04/developers-age/]

Los programadores más viejos siempre han sufrido de discriminación por la edad. Pero de acuerdo con los estudios realizados por investifadores de la North Carolina State University, las compañias deberían pensar dos veces contratar a un joven hacker en vez de un programador senior.

Emerson Murphy-Hill, profesor asistente de Ciencias de la Computación de la North Carolina State University y co-autor del estudio, dice que los programadores veteranos tienen más recorrido para las empresas de los que ellas pueden pensar. “Sabemos que ciertas aspectos se vuelven peores, como tu agudeza visual”, dice. “Pero no es todo malo. En algunas cosas eres mejor, como en inteligencia emocional y social”.

Dice que tendemos a pensar en que programar  es algo que sólo se practica cuando eres joven: pasas tus veintitantos trabajando 80 horas / semana,  y entonces lo dejas y te vuelves gestor o jefe de proyecto. Pero eso puede no ser la mejor manera de llevar la carrera de un programador.

Para determinar si los programadores se vuelven mejores o peores con la edad, los investigadores han buscado los programadores que están mejor en el ranking de StackOverflow, un site donde los programadores pueden preguntar y contestar sobre programación. Los usuarios valoran las respuestas de sus compañeros programadores, y entonces el site utiliza estas valoraciones para generar un puntuación de reputación de cada programador. Comparando estos rankings con la edad de los programadores, el estudio ha encontrado que estas valoraciones crecian cuando los programadores rondaban los 50 años.

El estudio también intento valorar los conocimientos de cada uno de los programadores haciendo un seguimiento de cuantos diferentes temas habian escrito. El estudio encontró que los programadores más jovenes responden a las cuestiones en un numero pequeño de áreas  ,y que el rango se amplia cuando los programadores son mayores en edad.

Finalmente, el estudio buscó cuantas preguntas contestaron los programadores sobre tecnologías de menos de 10 años de antiguedad, y se encontró que los programadores mayores tenían más  conocimiento que los más jóvenes sobre las nuevas plataformas móviles tales como iOS y Windows Phone. Para otras tecnologías, no habia un gap significativo entre mayores y jóvenes.

Los investigadores concluyeron que cualquier prejuicio sobre los programadores viejos no es soportado por los datos que ofrece StackOverflow. Pero este estudio tiene límites. Muchos usuarios de StackOverflow no informan de su edad, y parece que sólo los programadores viejos son los representados en el estudio, basándose en los datos del Bureau of Labor Statistics.

Los programadores mayores que utilizan el site pueden estar haciendo un esfuerzo consciente para conservar sus conocimientos técnicos actualizados y para promocionarse ellos mismos. O podrían utilizar el site porque ellos saben que son cultos en la materia, mientras que sus compañeros no tan cultos permanecen fuera , lo que falsearía los resultados. Y , desde luego, la reputación de las valoraciones StackOverflow  no necesariamente son correlativas a las habilidades técnicas.

The paper detailing the study, Is Programming Knowledge Related To Age?, will be presented at the Working Conference on Mining Software Repositories in San Francisco on May 18. But it’s just a start. In an effort to draw better conclusions, Murphy-Hill says his team hopes to look at a wider variety of programmer populations. He says they’re also interested in finding out why younger developers contribute more to open source than older developers.

Los amateurs se enfadan con los clientes, los profesionales los educan

[fuente: http://99u.com/articles/18303/we-deserve-the-clients-we-get]

Como la mayoría de los freelancers con experiencia saben, algunas veces tenemos que dejar de trabajar con clientes por nuestro beneficio y el suyo. Pero no tiene porqué suceder de esta manera.

Yo solía pensar que tratar con clientes frustrados era solo un asunto de tener mano izquierda. Pero entonces me di cuenta , que si bien es verdad que en todas las relaciones hay aspectos frustrantes, la frustración debería ser la excepción de la regla.

Hay momentos puntuales en el camino a convertirnos en la versión freelance de Donald Trump , en el que gritamos “‘¡No quiero trabajar contigo!” a cualquiera que esté en desacuerdo contigo. Aunque lo cierto es , que merecemos los clientes que tenemos. Los malos clientes no son el resultado de alguna fuerza cósmica que está en nuestra contra , sino que más bien son el resultado de nuestras propias acciones.

Los clientes frunstrantes son el resultado de algún paso en falso que hemos dado a lo largo del camino. Para hacer lo mejor que opdamos nuestro trabajo con la mejor gente posible, necesitamos ser diligentes (cuidadosos) en nuestras relaciones con nuestros clientes. Aquí os muestro como:

Tener los cojones de decir “no”

Si no es un buen proyecto para tí, alejate antes de que el dinero se meta por medio. ¿Es el tipo de proyecto por el que quieres ser conocido? Si estas llenando tu curriculum con trabajos en los que no estás interesado, todo lo que estás haciendo es atrayendo más de lo mismo (te recomiendo esta charla : Jason Santa Maria gave a great Creative Mornings talk , trata sobre el poder y el valor de decir no a trabajos). Puede costarte hacerlo , pero piensa que sólo es un cliente.

Huye antes de que haya dinero de por medio.

Deja suficientemente claro tus principios a todos

La forma más fácil de hacer esto es bloguear regularmente en el mismo website donde esté tu portfolio. Escribe honestamente sobre el  trabajo que haces. Eso muestra inmediatamente a los potenciales clientes si sus objetivos y sus valores se corresponden con los tuyos y asi ahorra tiempo si se da que tanto tú como tu cliente estan fuera de sincronía.

Educa a tus clientes

Oportunidades hay. Todos hemos formado parte de muchos proyectos en los que trabajamos cerca o con los propios clientes que nos contratan. Tenemos una gran oportunidad de enseñar a nuestros clientes lo que hemos aprendido de toda nuestra experiencia.

Si un cliente no está de acuerdo con algo que tu sabes que es lo correcto , no te desgastes intentando convencerlo. En su lugar, pon el modo “research”  o investigación. Muestrales algunos ejemplos de porqué lo que ellos quieren no funciona en el proyecto que tenéis entre manos. Y puede pasar que el cliente aún así te haga ver su punto de vista de modo que te convenza que su idea funcionará, en ese caso puedes ceder (y aprender algo en el proceso). Si ellos no consiguen convencerte acabas de apuntarte un tanto mientras les has educado para que vengan más futuros proyectos. Consideralo una inversión en un recurso que necesitas para que tu carrera profesional sea más exitosa.

Interroga a tus potenciales clientes

¿Cuáles son sus preferencias en diseño? ¿Se corresponde eso con el tipo de trabajos en los que estás interesado? No tiene sentido trabajar con un cliente que le gustan las campanas brillantes y los silbiditos si a ti te gustan hacer diseños minimalistas. Filtrar a los clientes te permite elegir los mejores para trabajar y te proporcionan el tipo de trabajo que tu te sientes más cómodo en los que das lo mejor de ti mismo.

Ser claro con los objetivos del proyecto

De esa forma no hay malentendidos, no es una asunto de lo que ellos quieren contra lo que tu quieres, lo cual es altamente subjetivo, es más un asunto de lo que cumple los objetivos del proyecto de la mejor manera. Pon estos objetivos escritos y tenlos como documento de referencia cuando sea necesario.

***

Es duro decir no a los clientes (y al dinero que te pueden hacer ganar), especialmente cuando estás empezando. Pero como otros tipos de emprendimientos creativos, enfocate en la calidad desde el principio y tu carrera será exponencialmente más fácil. Después de todo, los buenos clientes nos traen buenos trabajos , lo que nos hace más felices y realizados  (y menos quejosos a nuestra gente cercana sobre como nuestros clientes no dejan de tomar malas decisiones). Crear un cuerpo de trabajo con el que estés feliz puede llevarte toda una vida.

Nosotros somos los responsables del trabajo que ofrecemos al mundo, asi que por qué no hacemos ese trabajo bien hecho?

PAUL JARVIS

Paul is a Gentleman of Adventure. He’s also a web designer andauthor. If he’s not in nature on some Thoreau-esque tangent (but with wifi), you can find him on twitter at @pjrvs.

GAE: Tareas programadas con cron para Java

El servicio cron de App Engine te permite configurar tareas programadas regularmente que operan en momentos definidos o intervalos regulares. Estas tareas se conocen comúnmente como tareas cron. El servicio cron de App Engine activa automáticamente estas tareas cron. Por ejemplo, puedes utilizarlo para enviar un correo electrónico de informe a diario, para actualizar los datos almacenados en la caché cada 10 minutos o para actualizar la información de resumen cada hora.

Una tarea cron invocará una URL, mediante una solicitud GET HTTP, en un momento concreto del día. Una URL invocada por el servicio cron está sujeta a los mismos límites y cuotas que una solicitud HTTP normal, incluido el límite de tiempo de la solicitud. Las tareas cron se ejecutan únicamente para la versión predeterminada de tu aplicación.

Una aplicación puede contener hasta 20 tareas programadas.

Acerca de cron.xml

Un archivo cron.xml del directorio WEB-INF de la aplicación (junto con appengine-web.xml) controla el servicio cron de la aplicación Java. A continuación, se muestra un ejemplo del archivo cron.xml:

<?xml version="1.0" encoding="UTF-8"?>
<cronentries>
  <cron>
    <url>/recache</url>
    <description>Repopulate the cache every 2 minutes</description>
    <schedule>every 2 minutes</schedule>
  </cron>
  <cron>
    <url>/weeklyreport</url>
    <description>Mail out a weekly report</description>
    <schedule>every monday 08:30</schedule>
    <timezone>America/New_York</timezone>
  </cron>
</cronentries>

En el caso de que un XSD describa el formato, consulta el archivo docs/cron.xsd del SDK.

Un archivo cron.xml consta de varias definiciones de tarea. Una definición de tarea debe contener <url> y <schedule>. Opcionalmente, puedes especificar también<description> y <timezone>. La descripción se puede ver en la consola de administración.

El campo url es simplemente una URL de la aplicación. El formato del campo schedule se trata más detenidamente en la sección Formato de la programación.

La zona horaria debe tener el nombre de una zona horaria zoneinfo estándar. Si no especificas una zona horaria, las tareas se ejecutarán en la hora UTC (también conocida como hora GMT).

Formato de la programación

Las programaciones cron se especifican mediante un formato tipo inglés sencillo.

A continuación, se muestran ejemplos de programaciones:

every 5 minutes
every 12 hours
2nd,third mon,wed,thu of march 17:00
every monday 09:00
1st monday of sep,oct,nov 17:00
every day 00:00

Si no necesitas ejecutar una tarea repetida en un momento específico pero, necesitas ejecutarla en intervalos regulares, utiliza el siguiente formato: every N (hours|mins|minutes), donde N es un número y horas o minutos especifica la unidad de tiempo. El tiempo más breve que se puede especificar en la ejecución de una tarea es 1 minuto.

De forma predeterminada, una programación con intervalos comienza en el siguiente intervalo después de haberse completado la última tarea. Si quieres que las tareas comiencen en intervalos regulares durante el día independientemente del momento en que se completó la última tarea, añade la palabra synchronized a la descripción del intervalo. Por ejemplo:

every 2 hours synchronized

Esta configuración ejecuta una tarea 12 veces al día en intervalos de dos horas, a partir de la medianoche.

Si quieres establecer tiempos más específicos, puedes especificar la programación del siguiente modo:

("every"|ordinal) (days) ["of" (monthspec)] (time)

Los paréntesis se emplean solo a modo de ejemplo y las comillas indican un valor literal.

Donde:

  • ordinal indica una lista separada por comas de “1st”, “first” y así sucesivamente (ambas formas son correctas).
  • days indica una lista de días de la semana separados por comas (por ejemplo, “mon”, “tuesday”; se aceptan tanto la forma corta como la forma larga); “every day” equivale a “every mon,tue,wed,thu,fri,sat,sun”.
  • monthspec indica una lista de meses separados por comas (por ejemplo, “jan”, “march”, “sep”). Si se omite, implica todos los meses. También puedes incluir “month” para indicar todos los meses, como en “1,8,15,22 of month 09:00”.
  • time indica la hora del día, en formato HH:MM y horario de 24 horas.

Protección de direcciones URL para cron

Para evitar que los usuarios accedan a las URL utilizadas por las tareas programadas, puedes restringir el acceso a las cuentas del administrador. Las tareas programadas pueden acceder a las URL exclusivas de los administradores. Puedes obtener más información sobre cómo restringir las URL en la sección Seguridad y autenticación. A continuación, se muestra un ejemplo que puedes utilizar en web.xml para restringir todo lo que empiece por /cron/ únicamente a los administradores:

    <security-constraint>
        <web-resource-collection>
            <url-pattern>/cron/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>admin</role-name>
        </auth-constraint>
    </security-constraint>

Nota: aunque las tareas cron pueden utilizar rutas de URL restringidas con <role-name>admin</role-name>no pueden utilizar rutas de URL restringidas con <role-name>*</role-name>.

Para obtener más información sobre el formato de web.xml, consulta la documentación del descriptor de implementación.

Para probar una tarea cron, accede como administrador e introduce la URL del controlador en el navegador.

Las solicitudes procedentes del servicio cron contienen también una cabecera HTTP:

X-AppEngine-Cron: true

Si te quieres asegurar de que solo las solicitudes cron puedan activar el controlador, busca esta cabecera.

Versiones de cron y de la aplicación

Las solicitudes cron siempre se envían a la versión predeterminada de la aplicación.

Subida de tareas cron

Puedes utilizar AppCfg para subir las tareas cron. Cuando subas la aplicación a App Engine a través de AppCfg update, el servicio cron se actualizará con el contenido decron.xml. Puedes actualizar solo la configuración cron sin subir el resto de la aplicación a través de AppCfg update_cron.

Para eliminar todas las tareas cron, modifica el archivo cron.xml para que contenga solo lo siguiente:

<?xml version="1.0" encoding="UTF-8"?>
<cronentries/>

Compatibilidad del servicio cron con la Consola del administrador

La Consola del administrador te permite ver el estado de tus tareas cron. Selecciona el enlace “Tareas cron” en el menú lateral para ver el estado de las tareas, incluida la última vez que se ejecutó la tarea, así como su resultado.

Para ver cuándo se han añadido o eliminado las tareas cron, selecciona la página Registros del administrador en el menú de la Consola del administrador.

Compatibilidad del servicio cron con el servidor de desarrollo

El servidor de desarrollo no ejecuta automáticamente las tareas cron. Puedes utilizar el servicio cron del escritorio local o la interfaz de tareas programadas para acceder a las URL de las tareas a través de curl o de una herramienta similar.

Prototype Pattern in Java

[Fuente: http://www.journaldev.com/1440/prototype-pattern-in-java]

Prototype pattern is one of the Creational Design pattern, so it provides a mechanism of object creation. Prototype pattern is used when the Object creation is a costly affair and requires a lot of time and resources and you have a similar object already existing. So this pattern provides a mechanism to copy the original object to a new object and then modify it according to our needs. This pattern uses java cloning to copy the object.

It would be easy to understand this pattern with an example, suppose we have an Object that loads data from database. Now we need to modify this data in our program multiple times, so its not a good idea to create the Object using new keyword and load all the data again from database. So the better approach is to clone the existing object into a new object and then do the data manipulation.

Prototype design pattern mandates that the Object which you are copying should provide the copying feature. It should not be done by any other class. However whether to use shallow or deep copy of the Object properties depends on the requirements and its a design decision.

Here is a sample program showing implementation of Prototype pattern.

Employees.java
 
package com.journaldev.design.prototype;
 
import java.util.ArrayList;
import java.util.List;
 
public class Employees implements Cloneable{
 
    private List<String> empList;
     
    public Employees(){
        empList = new ArrayList<String>();
    }
     
    public Employees(List<String> list){
        this.empList=list;
    }
    public void loadData(){
        //read all employees from database and put into the list
        empList.add("Pankaj");
        empList.add("Raj");
        empList.add("David");
        empList.add("Lisa");
    }
     
    public List<String> getEmpList() {
        return empList;
    }
 
    @Override
    public Object clone() throws CloneNotSupportedException{
            List<String> temp = new ArrayList<String>();
            for(String s : this.getEmpList()){
                temp.add(s);
            }
            return new Employees(temp);
    }
     
}

Notice that the clone method is overridden to provide a deep copy of the employees list.

Here is the test program that will show the benefit of prototype pattern usage.
PrototypePatternTest.java
package com.journaldev.design.test;
 
import java.util.List;
 
import com.journaldev.design.prototype.Employees;
 
public class PrototypePatternTest {
 
    public static void main(String[] args) throws CloneNotSupportedException {
        Employees emps = new Employees();
        emps.loadData();
         
        //Use the clone method to get the Employee object
        Employees empsNew = (Employees) emps.clone();
        Employees empsNew1 = (Employees) emps.clone();
        List<String> list = empsNew.getEmpList();
        list.add("John");
        List<String> list1 = empsNew1.getEmpList();
        list1.remove("Pankaj");
         
        System.out.println("emps List: "+emps.getEmpList());
        System.out.println("empsNew List: "+list);
        System.out.println("empsNew1 List: "+list1);
    }
 
}-

Output of the above program is:

1
2
3
emps HashMap: [Pankaj, Raj, David, Lisa]
empsNew HashMap: [Pankaj, Raj, David, Lisa, John]
empsNew1 HashMap: [Raj, David, Lisa]

If the object cloning was not provided, every time we need to make database call to fetch the employee list and then do the manipulations that would have been resource and time consuming.

Builder Design Pattern in Java

[Fuente: http://www.journaldev.com/1425/builder-design-pattern-in-java]

Builder design pattern is a creational design pattern like Factory Pattern and Abstract Factory Pattern. This pattern was introduced to solve some of the problems with Factory and Abstract Factory design patterns when the Object contains a lot of attributes.

There are three major issues with Factory and Abstract Factory design patterns when the Object contains a lot of attributes.

  1. Too Many arguments to pass from client program to the Factory class that can be error prone because most of the time, the type of arguments are same and from client side its hard to maintain the order of the argument.
  2. Some of the parameters might be optional but in Factory pattern, we are forced to send all the parameters and optional parameters need to send as NULL.
  3. If the object is heavy and its creation is complex, then all that complexity will be part of Factory classes that is confusing.

We can solve the issues with large number of parameters by providing a constructor with required parameters and then different setter methods to set the optional parameters but the problem with this is that the Object state will be inconsistent until unless all the attributes are set explicitly.

Builder pattern solves the issue with large number of optional parameters and inconsistent state by providing a way to build the object step-by-step and provide a method that will actually return the final Object.

Builder Pattern Implementation

  1. First of all you need to create a static nested class and then copy all the arguments from the outer class to the Builder class. We should follow the naming convention and if the class name is Computer then builder class should be named as ComputerBuilder.
  2. The Builder class should have a public constructor with all the required attributes as parameters.
  3. Builder class should have methods to set the optional parameters and it should return the same Builder object after setting the optional attribute.
  4. The final step is to provide a build() method in the builder class that will return the Object needed by client program. For this we need to have a private constructor in the Class with Builder class as argument.

Here is the sample code where we have a Computer class and ComputerBuilder class to build it.

 
package com.journaldev.design.builder;
 
public class Computer {
     
    //required parameters
    private String HDD;
    private String RAM;
     
    //optional parameters
    private boolean isGraphicsCardEnabled;
    private boolean isBluetoothEnabled;
     
 
    public String getHDD() {
        return HDD;
    }
 
    public String getRAM() {
        return RAM;
    }
 
    public boolean isGraphicsCardEnabled() {
        return isGraphicsCardEnabled;
    }
 
    public boolean isBluetoothEnabled() {
        return isBluetoothEnabled;
    }
     
    private Computer(ComputerBuilder builder) {
        this.HDD=builder.HDD;
        this.RAM=builder.RAM;
        this.isGraphicsCardEnabled=builder.isGraphicsCardEnabled;
        this.isBluetoothEnabled=builder.isBluetoothEnabled;
    }
     
    //Builder Class
    public static class ComputerBuilder{
 
        // required parameters
        private String HDD;
        private String RAM;
 
        // optional parameters
        private boolean isGraphicsCardEnabled;
        private boolean isBluetoothEnabled;
         
        public ComputerBuilder(String hdd, String ram){
            this.HDD=hdd;
            this.RAM=ram;
        }
 
        public ComputerBuilder setGraphicsCardEnabled(boolean isGraphicsCardEnabled) {
            this.isGraphicsCardEnabled = isGraphicsCardEnabled;
            return this;
        }
 
        public ComputerBuilder setBluetoothEnabled(boolean isBluetoothEnabled) {
            this.isBluetoothEnabled = isBluetoothEnabled;
            return this;
        }
         
        public Computer build(){
            return new Computer(this);
        }
 
    }
 
}

Notice that Computer class has only getter methods and no public constructor, so the only way to get a Computer object is through the ComputerBuilder class.

Here is a test program showing how to use Builder class to get the object.

package com.journaldev.design.test;
 
import com.journaldev.design.builder.Computer;
 
public class TestBuilderPattern {
 
    public static void main(String[] args) {
        //Using builder to get the object in a single line of code and
                //without any inconsistent state or arguments management issues     
        Computer comp = new Computer.ComputerBuilder(
                "500 GB", "2 GB").setBluetoothEnabled(true)
                .setGraphicsCardEnabled(true).build();
    }
 
}

Builder Design Pattern Example in JDK

  • java.lang.StringBuilder#append() (unsynchronized)
  • java.lang.StringBuffer#append() (synchronized)

Los 7 errores que debes evitar al buscar trabajo en Internet

[Fuente: http://www.trabajandoatope.es/los-7-errores-que-debes-evitar-al-buscar-trabajo-en-internet/?goback=%2Egde_3730479_member_5810665777761644544#%21]

 

PERFIL INCOMPLETO

Cuando uno se dispone a buscar trabajo en Internet es muy importante abrirse una cuenta en los portales de empleo donde puedes encontrar trabajo relacionado con tu formación y experiencia. Como consejo, intenta ir más allá de los típicos portales como Infojobs o Infoempleo a donde va todo el mundo. Además estos portales de empleo resultan demasiado generales por lo que encontrarás menos ofertas específicas de tu sector. Investiga cuáles son aquellas webs que cuentan con un número de ofertas de trabajo mayor en tu campo.

Una vez lo hayas hecho, rellena bien tu perfil. Tómate tiempo pues esta es la parte más importante, ya que un curriculum incompleto, además de no dar toda la información necesaria sobre tu perfil dará una imagen de dejadez que no te beneficia en nada. Por eso hasta que no lo hayas hecho no te inscribas para ningún puesto ofertado, pues estarás perdiendo una oportunidad.

 

NO TE CONVIERTAS EN MISTER ELÁSTICO

Como ya he dicho en otras ocasiones, si cometemos errores en nuestro curriculum o en nuestra manera de postular es muy posible que no nos llamen para la entrevista de trabajo y lo más probable es que acabemos frustrados y echando nuestro curriculum a todo lo que se mueva. Es lo que llamamos ser Mister Elástico. Sin duda uno de los mayores errores en los que puedes caer. Y es que la facilidad de poder hacerlo con un sólo click resulta muy tentador en algunos casos, incluso aun sabiendo que no cumplimos con los requisitos. Pensamos tontamente “a ver si suena la flauta”. Sin embargo, ¿realmente crees que con la cantidad de parados que hay actualmente va a sonar la flauta? Si no cumples los requisitos, tranquilo, siempre habrá alguien que sí los cumpla.

business graphics 1428642  Los 7 errores que debes evitar al buscar trabajo en Internet

Por eso es importante que valores tu perfil y que si no cumples con al menos el 95% de los requisitos exigidos, ni te molestes en enviar tu curriculum pues estarás perdiendo un tiempo precioso que podrías estar dedicando a personalizarlo para conseguir mejores resultado al buscar trabajo en Internet. Sin duda, esto te dará más resultados que postular a mil trabajos para los que no tienes ni formación ni experiencia.

 

NO CUIDAR TU IMAGEN EN LA RED

Seguro que cuando te interesa mucho una oferta de trabajo intentas conocer más información acerca de la empresa. Pues esto mismo ocurre cuando una empresa se interesa por tu perfil. Y es que es la forma más sencilla y rápida de conocerte un poco más y poder valorar si eres el candidato idóneo para el puesto y por tanto mereces demostrarlo en la entrevista de trabajo es poniendo tu nombre en Internet. Si eres miembro de alguna o varias redes sociales, es evidente que aparecerá tu perfil entre los resultados de Google.

Por eso no está de más que lo hagas tú mismo y revises si hay datos que puedan comprometerte o boicotear tu candidatura a la hora de buscar trabajo en Internet. Si te interesa el tema puedes leer algunos posts relacionados en la sección “Trabajo y redes sociales”.

 

PASIVIDAD

Si te limitas a echar curriculum solamente en las ofertas de trabajo que ves en los portales de empleo de Internet, estarás perdiéndote muchas oportunidades. Puedes darte cuenta de ello si lees “Ofertas de trabajo VS Candidatura espontánea”. Pero además de buscar trabajo en Internet es importante que no estés parado, sino que realices algún tipo de formación relacionada con tu campo para aumentar tu curriculum. pero también es una buena idea hacer algún cursos sobre aquellas disciplinas más demandadas ahora en el mercado como por ejemplo los idiomas. De esta forma podrás justificar mucho mejor tu inactividad si finalmente estás mucho tiempo en el paro (leer post).

 

NO CUIDAR LOS CONTACTOS EN LAS REDES SOCIALES

Existen muchas redes sociales enfocadas sobre todo a buscar trabajo en Internet por lo que cuentan con un fin claramente profesional. Puedes conocer algunas  de ellas en “Las 6 mejores redes sociales para encontrar trabajo”. Por eso es recomendable que sepas distinguir entre tu entorno personal y profesional. Y es que los departamentos de RR.HH. pueden contactarse entre ellos para conseguir información sobre los candidatos a una oferta de trabajo.

Pero además es importante que cuides tu red de contactos para que pueda servirte de ayuda en tu búsqueda de empleo. Eso sí, como ya he dicho otras veces las redes sociales son una carrera de fondo y no puedes esperar tener un montón de contactos que den la cara por ti para recomendarte en un empleo, si apareces y desapareces a intervalos según tienes empleo o no. Este es uno de los mayores errores, pues es algo que debes cultivar a diario.

90 Los 7 errores que debes evitar al buscar trabajo en Internet

 

DEJAR PASAR LAS NUEVAS OPORTUNIDADES DEL CV ONLINE

Aunque tradicionalmente siempre se ha dicho que lo máximo que debías ocupar eran 2 hojas en tu curriculum, en la actualidad Internet ha cambiado mucho las cosas. La facilidad con la que puede verse un perfil en unos pocos segundos bajando el cursor y el no ocupar nada de espacio en papel hacen que sea posible poder escribir al detalle nuestra formación y nuestra experiencia laboral. De esta forma podremos dar una imagen más acertada de quiénes somos. Por eso debemos aprovechar esta oportunidad al máximo para decir todo lo que queramos y que pensemos que pueda resultar de valor para el puesto de trabajo.

 

DESCARTAR LA VÍA OFFLINE

No obstante aunque centres tu búsqueda en la red, no caigas en uno de loserrores que más cometen las personas al buscar trabajo en Internetque es el de menospreciar la búsqueda de empleo tradicional. Y es que todos los caminos son útiles. No te dejes llevar por la comodidad. De hecho si quieres conseguir tu objetivo, debes agotar todas las vías hasta encontrar trabajo. Por eso si te interesa mucho una oferta de trabajo, no dudes en presentarte en persona en al empresa, ya que si en ese momento tienen un hueco es probable que quieran hacerte una entrevista, algo que no habría ocurrido si te hubieras quedado en casa.

Pero además es importante que todo tu entorno, es decir, tus amigos y familiares se enteren de que estás buscando empleo para que puedan echarte una mano en el caso de que se enteren de alguna oferta de trabajo que encaje con tu perfil.

 

Y aunque no podemos asegurarte que evitar cometer estos errores al buscar trabajo en Internet vaya a garantizarte el éxito, si que al menos estarás evitando echarte tierra encima. No obstante, la realidad es que si eres constante y planificas bien tu búsqueda, tarde o temprano acabarás recogiendo los frutos.

Cómo informar de errores de forma efectiva

[Fuente: http://www.chiark.greenend.org.uk/~sgtatham/bugs-es.html]

por Simon Tatham, programador profesional y de software libre

Cualquiera que haya escrito software para uso público probablemente haya recibido al menos un mal informe de fallo. Informes que no dicen nada (“¡No funciona!”); informes que no tienen sentido; informes que no dan información suficiente; informes que dan información errónea. Informes de problemas que acaban siendo un error del usuario; informes de problemas que acaban siendo un fallo del programa de un tercero; informes de problemas que acaban siendo fallos en la red.

Existe una razón por la cual el soporte técnico se ve como un empleo horrible, y esa razón son los malos informes de fallo. Sin embargo, no todos los informes de fallo son molestos: yo mantengo software libre, cuando no me estoy ganando la vida, y a veces recibo informes de fallos increíblemente claros, útiles e informativos.

En este ensayo intentaré dejar claro lo que hace bueno a un informe de fallo. Idealmente me gustaría que todo el mundo se leyera este ensayo antes de informar de un fallo a cualquiera. Ciertamente, me gustaría que todos los que vayan a informarme de fallos a  se lo leyesen.

En pocas palabras, el objetivo de un informe de fallo es permitir al programador ver el programa fallando ante sus ojos. Puedes mostrárselo en persona, o darle instrucciones cuidadas y detalladas de cómo hacer que falle. Si pueden hacer que falle, podrán intentar recolectar información extra hasta que averigüen la causa. Si no pueden hacer que falle, tendrán que pedirte que tú recolectes esa información en su lugar.

En los informes de fallo, intenta dejar muy claro qué son hechos reales (“Estaba con la computadora y ocurrió esto”) y qué son especulaciones (“Creo que esto puede ser el problema”). Excluye las especulaciones si quieres, pero no excluyas nunca los hechos.

Cuando informas de un fallo, lo haces porque quieres que el fallo se arregle. No tiene sentido blasfemar al programador o ser deliberadamente inútil: puede que sea culpa suya que tú tengas el problema, y puede que tengas razón al estar enfadado con ellos, pero el fallo se arreglará antes si les ayudas proporcionándoles toda la información que necesitan. Recuerda también que si el programa es gratuito, entonces el autor lo proporciona amablemente, de modo que si mucha gente fuera ruda con ellos quizás deban dejar de sentirse amables.

 

“No funciona.”

Dale al programador algo de crédito en cuanto a inteligencia básica: si el programa no funcionase en absoluto, se hubieran dado cuenta. Si no se han dado cuenta, es que en su caso funciona. Por tanto, o bien estás haciendo algo diferente de lo que ellos hacen, o tu entorno es diferente del suyo. Necesitan información; proporcionar esa información es el objetivo de un informe de fallo. Más información casi siempre es mejor que menos.

Muchos programas, particularmente programas gratuitos, publican una lista de fallos conocidos. Si puedes encontrar una lista de fallos conocidos, merece la pena leerla para ver si el fallo que acabas de encontrar ya es conocido o no. Si lo es, probablemente no merezca la pena informar de él nuevamente, pero si piensas que tienes más información que la que aparece en el informe de fallo, puede que quieras contactar con el programador de todos modos. Puede que sean capaces de arreglar el fallo más fácilmente si les das información que todavía no tenían.

Este ensayo está lleno de consejos. Ninguno de ellos es una regla absoluta. Programadores concretos tienen formas concretas en las que les gusta que se les informe de fallos. Si el programa viene con su propio conjunto de consejos para informar de fallos, léelos. Si los consejos que vienen con el programa contradicen los consejos presentes en este ensayo, ¡sigue los que vienen con el programa!

Si no estás informando de un fallo sino simplemente pidiendo ayuda para utilizar el programa, deberías indicar en qué otros sitios has buscado ya la respuesta a tu pregunta. (“Miré el capítulo 4 y la sección 5.2 de la ayuda pero no encontré nada que me dijera si esto es posible.”) Esto permitirá que el programador sepa dónde espera la gente encontrar la respuesta, de forma que pueden hacer que la documentación sea más fácil de usar.

 

“Muéstrame.”

Una de las mejores maneras de informar de un fallo es mostrarlo al programador. Sitúalos delante de tu ordenador, lanza su software, y demuestra aquello que funciona mal. Deja que miren cómo arrancas la máquina, que miren cómo ejecutas el software, que miren cómo interactúas con el software y que miren lo que el software realiza como respuesta a tus entradas.

Conocen su software como la palma de su mano. Saben en qué partes confiar, y saben qué partes pueden tener más fallos. Saben intuitivamente lo que tienen que buscar. Cuando el software haya hecho algo obviamente erróneo, puede que hayan reparado en algo anterior sutilmente erróneo que pueda darles una pista. Pueden observar todo lo que el computador hace durante la ejecución de prueba, y pueden decidir lo que es importante por sí mismos.

Quizás esto no sea suficiente. Quizás decidan que necesitan más información, y te pidan que les muestres lo mismo una vez más. Puede que te pidan que les guíes en el proceso, de forma que puedan reproducir el fallo por sí mismos todas las veces que sean necesarias. Puede que intenten variar el proceso algunas veces más, y así conocer si el problema ocurre sólo en un caso o en una familia de casos relacionados. Si no tienes suerte, quizás necesiten sentarse durante un par de horas con un conjunto de herramientas de desarrollo y empezar a investigar de verdad. Pero lo más importante es que el programador esté mirando el computador cuando algo falla. Una vez que puedan ver el problema, normalmente podrán seguir solos a partir de ese punto para intentar arreglarlo.

 

“Enséñame a mostrar por mí mismo.”

Ésta es la era de Internet. Es la era de las comunicaciones globales. Es la era en la que puedo enviar mi software a alguien de Rusia pulsando un botón, y él puede enviarme sus comentarios de forma igualmente sencilla. Pero si tiene un problema con mi programa, no puede hacer que yo esté presente cuando falla. “Muéstrame” es bueno cuando puedes, pero muchas veces no puedes.

Si tienes que informar de un fallo a un programador que no puede estar presente personalmente, el objetivo del ejercicio es hacer que sean capaces de reproducir el problema. Quieres que el programador ejecute su propia copia del programa, haga lo mismo que tú, y haga que falle de la misma manera. Cuando no pueden ver que el problema ocurre delante de sus ojos, no pueden hacer nada con él.

Así que diles exactamente lo que hiciste. Si es un programa gráfico, diles qué botones pulsaste y en qué orden los pulsaste. Si es un programa que se ejecuta escribiendo una orden, múestrales de forma precisa la orden que usaste. Cuando sea posible, debes proporcionar una transcripción de la sesión, mostrando las órdenes que tecleaste y lo que la computadora mostró como respuesta.

Dale al programador toda la información que se te ocurra. Si el programa lee de un fichero, probablemente necesites enviarles una copia del fichero. Si el programa habla con otro computador a través de una red, probablemente no puedas enviarles una copia de ese computador, pero puedes al menos decir qué clase de computador es y, si es posible, qué software ejecuta.

 

“A mí me funciona. ¿Qué está mal?”

Si le das a un programador una larga lista de entradas y acciones y ellos lanzan su propia copia del programa y no ocurre nada malo, entonces es que no les has dado información suficiente. Posiblemente el fallo no ocurre en todos los computadores; tu sistema y su sistema puede diferir de algún modo. Quizás hayas malentendido lo que el programa supuestamente hace, y ambos estáis mirando exactamente la misma pantalla y tú piensas que está mal y ellos saben que está bien.

Así que describe también lo que ocurrió. Diles exactamente lo que viste. Diles por qué piensas que lo que viste está mal; aún mejor, diles exactamente lo que tú esperabas ver. Si dices “y entonces falló”, estás dejando fuera información muy importante.

Si viste mensajes de error entonces dile al programador, de forma clara y precisa, cuáles eran. ¡Son importantes! En este momento, el programador no está intentando arreglar el problema: sólo están intentando encontrarlo. Necesitan saber qué ha ido mal, y esos mensajes de error son el mayor esfuerzo del computador para decírtelo. Anota los errores si no tienes otra forma más sencilla de recordarlos, pero no merece la pena informar de que el programa generó un error a menos que puedas indicar cuál era el mensaje de error.

En particular, si el mensaje de error contiene números, asegúrate de informar de ellos al programador. Aunque tú no puedas encontrarles sentido no significa que no lo tengan. Los números contienen toda clase de información que puede ser leída por un programador, y probablemente contengan pistas vitales. Los números en los mensajes de error están ahí porque el computador está demasiado confundido como para informar del fallo con palabras, pero está intentando hacerlo lo mejor posible para darte la información importante.

En este punto, el programador está a efectos prácticos realizando el trabajo de un detective. No saben lo que ha ocurrido, y no pueden estar lo bastante cerca como para verlo por ellos mismos, así que están intentando encontrar pistas que les hagan ver el problema. Los mensajes de error, las incomprensibles cadenas de números, e incluso los retardos inesperados son todos tan importantes como las huellas dactilares en la escena de un crimen. ¡Guárdalos!

Si usas Unix, el programa puede producir un volcado de memoria. Los volcados de memoria son una fuente de pistas particularmente buena, así que no los elimines. Por otra parte, a la mayoría de programadores no les gusta recibir ficheros gigantes por correo electrónico sin una advertencia, así que pregunta antes de enviar un volcado a nadie. También, ten en cuenta que los volcados de memoria contienen un registro del estado completo del programa: cualquier “secreto” presente (quizás el programa estuviera manejando un mensaje personal, o trabajando con datos confidenciales) puede aparecer en el fichero del volcado.

 

“Y entonces intenté…”

Hay un montón de cosas que puedes hacer cuando aparece un fallo o un error. Muchas de esas cosas empeoran el problema. Una amiga mía en la escuela borró accidentalmente todos sus documentos de Word, y antes de pedir ayuda a un experto intentó reinstalar el Word y luego ejecutar el desfragmentador de disco. Ninguna de esas acciones ayudó a recuperar sus ficheros, y gracias a ambas el disco se dispersó de tal manera que ningún programa de recuperación hubiese podido hacer nada. Si no hubiese tocado nada, puede que hubiera tenido una oportunidad.

Usuarios como éste son como una mangosta atrapada en una esquina: con su espalda contra la pared y viendo la muerte inminente ante sus ojos, ataca salvajemente, porque hacer algo tiene que ser mejor que no hacer nada. Esto no se adapta bien al tipo de problemas que producen los computadores.

En lugar de ser una mangosta, sé un antílope. Cuando un antílope se enfrenta a algo inesperado o terrorífico, se para. Permanece completamente parado e intenta no atraer la atención, mientras se detiene a pensar y calcular qué es lo mejor que puede hacer. (Si los antílopes tuvieran una línea de atención técnica, probablemente llamarían a ella en ese momento.) Entonces, una vez que ha decidido qué es lo más seguro que puede hacer, lo hace.

Cuando algo va mal, inmediatamente deja de hacer nada. No toques ningún botón en absoluto. Mira la pantalla e intenta encontrar cosas fuera de lo normal, y recuérdalo o anótalo. Entonces quizás empieza cautelosamente a pulsar “Aceptar” o “Cancelar”, lo que te parezca más seguro. Intenta desarrollar un reflejo – si el computador hace algo inesperado, detente.

Si logras salir del problema, bien cerrando el programa afectado o bien reiniciando la computadora, sería bueno intentar hacer que el problema ocurra de nuevo. A los programadores les gustan los problemas que pueden reproducir más de una vez. Los programadores felices arreglan los fallos antes y de forma más eficiente.

 

“Creo que el modulador de taquiones tiene la polarización incorrecta.”

No sólo son los no-programadores los que producen malos informes de fallos. Algunos de los peores informes de fallos que he visto vienen de programadores, e incluso de buenos programadores.

Una vez trabajé con otro programador que no paraba de encontrar fallos en su código e intentaba arreglarlos. De vez en cuando encontraba un fallo que no era capaz de arreglar, y me llamaba para que le ayudase. “¿Qué ha pasado?”, le preguntaba. Él me respondía diciéndome su opinión sobre lo que necesitaba repararse.

Esto funcionaba bien cuando su opinión era correcta. Significaba que ya había hecho la mitad del trabajo y así podíamos terminarlo los dos juntos. Era eficiente y útil.

Sin embargo, muchas veces se equivocaba. Trabajábamos un tiempo intentando averiguar por qué una cierta parte del programa producía datos incorrectos, y eventualmente descubríamos que no los producía, y que habíamos estado media hora investigando un trozo de código que funcionaba perfectamente, y que el problema estaba en otra parte.

Estoy seguro de que tú no harías eso con un doctor. “Doctor, necesito que me prescriba hidroyoyodina.” La gente sabe que no debe hacer eso con un doctor: le describes los síntomas, las molestias y achaques y dolores y erupciones y fiebres, y dejas que el doctor haga el diagnóstico del problema y decida qué hacer al respecto. En otro caso el doctor te tomará por hipocondriaco y lunático, y con razón.

Ocurre lo mismo con los programadores. Proporcionar tu propio diagnóstico puede que a veces sea útil, pero siempre establece los síntomas. El diagnóstico es un extra opcional, y no una alternativa a describir los síntomas. Igualmente, enviar modificaciones del código para arreglar el problema es un suplemento útil en un informe de fallo, pero no un sustituto adecuado para el mismo.

Si un programador te pide información extra, ¡no te la inventes! Una vez alguien me informó de un fallo, y le pedí que ejecutara una cierta orden que yo sabía que fallaría. Le pedí que lo probase porque quería saber cuál de dos errores posibles surgiría. Conocer cuál era una pista vital. Pero no lo intentó – sino que me respondió diciendo “No, eso no va a funcionar”. Me llevó un tiempo persuadirle de que lo intentara de verdad.

Usar tu inteligencia para ayudar al programador está bien. Incluso si tus deducciones son incorrectas, el programador debería estar agradecido de que al menos hayas intentado hacerle la vida más fácil. Pero informa de los síntomas también, o quizás se la compliques.

 

“Vaya, lo hacía hace un momento.”

Dile “fallo intermitente” a un programador y fíjate la cara que ponen. Los problemas fáciles son aquellos en los que la realización de una secuencia simple de acciones hacen que surja el problema. El programador puede repetir esa secuencia de acciones en un entorno de pruebas cerrado y observable y ver lo que ocurre con gran detalle. Demasiados problemas no funcionan así: habrá programas que fallarán una vez a la semana, o una vez cada mucho tiempo, o nunca fallarán cuando los pruebes delante del programador pero sí cuando una fecha límite está a la vuelta de la esquina.

La mayoría de los fallos intermitentes no son verdaderamente intermitentes. La mayoría de ellos tienen alguna lógica en alguna parte. Algunos pueden ocurrir cuando la máquina se está quedando sin memoria, otros pueden ocurrir cuando otro programa intenta modificar un fichero crítico en el momento equivocado, y algunos pueden ocurrir ¡sólo en la primera media hora de cada hora! (Una vez vi uno de éstos).

También, si puedes reproducir el fallo pero el programador no puede, podría ser porque tu computador y su computador son diferentes en algo y ese algo es lo que provoca el fallo. Una vez tuve un programa cuya ventana se enrollaba en una pequeña bola en la esquina de la pantalla, se quedaba ahí y se enfurruñaba. Pero sólo hacía eso en pantallas de 800×600; en mi pantalla de 1024×768 estaba bien.

El programador querrá saber todo lo que puedas contarle del problema. Pruébalo en otra máquina, quizás. Pruébalo dos o tres veces y mira la frecuencia con la que falla. Si falla cuando estás trabajando en algo serio pero no cuando estás intentando demostrarlo, puede que sean los tiempos grandes de ejecución o ficheros grandes lo que lo hagan fallar. Intenta recordar todos los detalles que puedas acerca de lo que estabas haciendo cuando el programa falló, y si encontraste algún tipo de patrón, menciónalo. Cualquier información que puedas proporcionar será útil. Incluso si es sólo probabilística (como “falla con más frecuencia si estoy ejecutando el Emacs”), puede que no proporcione pistas directas acerca de la causa del problema, pero puede que ayude al programador a la hora de reproducirlo.

Más importante, el programador querrá estar seguro de si es un auténtico fallo intermitente o un fallo específico de esa máquina. Querrán conocer un montón de detalles acerca de tu computador, para intentar saber cómo difiere del suyo. Muchos de esos detalles dependerán del programa en particular, pero algo que deberías estar listo para proporcionar son los números de versión. El número de versión del propio programa, el número de versión del sistema operativo, y probablemente de cualquier otro programa que intervenga en el problema.

“Entonces cargué el disco en Windows…”

Escribir de forma clara es esencial en un informe de fallo. Si el programador no puede entenderte, quizás sea mejor no decir nada.

Recibo informes de fallos de muchas partes del mundo. Muchos de ellos de personas que no hablan inglés nativamente, y muchos de esos se disculpan por su pobre nivel de inglés. En general, los informes con disculpas por el pobre nivel de inglés son en realidad muy claros y útiles. La mayoría de informes confusos provienen de hablantes nativos de inglés que suponen que les entenderé aunque no hagan ningún esfuerzo por ser claros o precisos.

  • Sé específico. Si puedes hacer lo mismo de dos maneras, di cuál usaste. “Seleccioné Cargar” puede significar “Pulsé sobre Cargar” o “Pulsé Alt+L”. Di cuál usaste. A veces importa.
  • Se prolijo. Da más información en lugar de menos. Si dices muchas cosas, el programador puede ignorar parte de ellas. Si dices pocas, tienen que hacerte más preguntas. Una vez recibí un informe de fallo de una sola frase; cada vez que preguntaba más información, el informador respondía con otra frase suelta. Me llevó varias semanas obtener un volumen de información útil, porque respondía con una frase corta de cada vez.
  • Ten cuidado con los pronombres y los verbos sin sujeto. No uses referencias como “la ventana”, cuando no está claro lo que significa. Considera el siguiente ejemplo: “Lancé la AplicaciónCualquiera. Apareció una ventana con una advertencia. Intenté cerrarla y cascó.” No está claro lo que el usuario intentó cerrar. ¿Intentó cerrar la ventana de advertencia o la ventana de AplicaciónCualquiera? Hay una diferencia. En lugar de eso, podría decir “Lancé la AplicaciónCualquiera, la cual mostró una ventana de advertencia. Intenté cerrar la ventana de advertencia y AplicaciónCualquiera cascó.” Esto es más largo y repetitivo, pero es más claro y más difícil de malinterpretar.
  • Lee lo que has escrito. Léete el informe a ti mismo e intenta ver si  crees que es claro. Si listas una secuencia de acciones a realizar para provocar el fallo, intenta seguirlas tú mismo para comprobar si has omitido algún paso.

Resumen

  • El primer objetivo de un informe de fallo es permitir que el programador vea el fallo por sí mismo. Si no puedes hacer que el programa falle delante de ellos, dale instrucciones detalladas para que ellos mismos puedan hacer que falle.
  • Si esto no surte efecto, y el programador no puede verlo fallar por sí mismo, el segundo objetivo del informe de fallo es describir lo que falló. Describe todo en detalle. Describe lo que viste, y lo que esperabas ver. Toma nota de los mensajes de error, especialmente cuando contengan números.
  • Cuando el computador haga algo inesperado, detente. No hagas nada hasta que estés tranquilo, y no hagas nada que creas que es peligroso.
  • Por todos los medios, intenta diagnosticar el problema tú mismo si crees que puedes, pero si lo haces, informa también de los síntomas igualmente.
  • Prepárate para proporcionar información extra si el programador la necesita. Si no la necesitasen no preguntarían por ella. No es que estén siendo retorcidos. Ten los números de versión preparados, porque probablemente harán falta.
  • Escribe de manera clara. Di lo que quieres decir, y asegúrate de que no se puede malinterpretar.
  • Por encima de todo, sé preciso. A los programadores les gusta la precisión.