Very High Activity

News

  Analyzed 3 days ago based on code collected 3 days ago.
 
Posted 4 days ago
In general there is a tendency to set parallel implementations to being equal
to performant implementations. Except in the really naive case there is always
going to be some overhead due to scheduling work, managing memory sharing ... [More] and
network communication overhead. Essentially that knowledge is reflected in
Amdahl’s law (the amount of serial work limits the benefit from running parts
of your implementation in parallel, http://en.wikipedia.org/wiki/Amdahl’s_law),
and Little’s law (http://en.wikipedia.org/wiki/Little’s_law) in case of queuing
problems.

When looking at current Java optimisations there is quite a bit going on to
support better parallelisation: Work is being done to provide for improving
lock contention situations, the GC adaptive sizing policy has been improved to
a usable state, there is added support for parallel arrays and lampbda’s
splitable interface.

When it comes to better locking optimisations what is most notable is work
towards coarsening locks at compile and JIT time (essentially moving locks from
the inside of a loop to the outside); eliminating locks if objects are being
used in a local, non-threaded context anyway; and support for biased locking
(that is forcing locks only when a second thread is trying to access an
object). All three taken together can lead to performance improvements that
will almost render StringBuffer and StringBuilder to exhibit equal performance
in a single threaded context.

For pieces of code that suffer from false sharing (two variables used in
separate threads independently that end up in the same CPU cacheline and as a
result are both flushed on update) there is a new annotation: Adding the
“@contended” annotation can help the compiler for which pieces of code to add
cacheline padding (or re-arrange entirely) to avoid that false sharing from
happening. One other way to avoid false sharing seems to be to look for class
cohesion - coherent classes where methods and variables are closely related
tend to suffer less from false sharing. If you would like to view the resulting
layout use the “-XX:PrintFieldLayout” option.

Java 8 will bring a few more notable improvements including changes to the
adaptive sizing GC policy, the introduction of parallel arrays that allow for
parallel execution of predicates on array entries, changes to the concurrency
libraries, internalised iterators. [Less]
Posted 4 days ago
Here's a nice slide deck on general benchmarking principles that I came across recently: Tokutek benchmarking principles

For hard-core perf types, there's not much new here, but I thought it was worth passing along in case some found it ... [More] interesting.

Among my favorite observationss from the slide deck:

benchmark frequently, to catch performance regressions soon after they occurkeep a benchmark history, for analysis of data over timeuse graphs and plots to help with data interpretationshare benchmarking data widely ("developers love data")At various companies, at various times, I've worked with benchmarking teams, and it's nice to see the slow-but-steady "systematization of knowledge" going on here, since some of these are hard lessons and it's worth your time not to re-learn them yourself. [Less]
Posted 4 days ago
This extension will be available in CXF release 2.7.6 which is not yet available. But you can run tests with the SNAPSHOT build till this version is released. Please provide feedback to the CXF mailing list.Different logging frameworks (SLF4J, Log4J ... [More] , Logback, JUL) can be used to log events for Apache CXF STS. The configuration allows to define which logger should log messages till to which log level. That works fine to drill down generic issues but it doesn't help too much to know whether a certain user could successfully log in or had any specific issues. Further, the WS-Trust interface is very generic. Therefore, the same user can request tokens but for different applications using different credential types. If a log in error occurs some context information is required to easily drill down user specific issues.

Based on the experience of a customer deployment, the following information is helpful to figure out how often a user requested a token and under which circumstances:

AppliesTo
For which application did the user request a tokenSource IP
From which machine did the application request a token for a userClaims
Which claims did the user request for an applicationSecurity Header
How did the user try to log in (Username/password, Kerberos, X509, ...)Realm
For which security domain did the user request a tokenetc. All this information are available within the core classes of the STS and thus not customizable without patching these classes. The next release of CXF will provide a customizable logging/auditing functionality to fulfill various requirements. Spring EventingThe Spring framework provides an eventing mechanism which is designed for simple communication between Spring beans. Instead of introducing a new eventing mechanism to push data to a class which processes the data and writes it to a log file the new feature leverages the usage of the Spring framework in the CXF STS. How Spring eventing works is described on the following blog. If you don't want to delay STS related processing you can publish the events asynchronously which is described here. CXF STS custom Application EventsDepending on the STS operation called, a different object with context information is created in the CXF STS. The following table summarizes the defined bindings in the WS-Trust specification, the CXF related context object as well a link with more information about this binding:

BindingContext objcectSpring EventDocumentationIssueTokenProviderParametersSTSIssueSuccessEvent
STSIssueFailureEventblogValidateTokenValidatorParametersSTSValidateSuccessEvent
STSValidateFailureEventblogCancelTokenCancellerParametersSTSCancelSuccessEvent
STSCancelFailureEventblogRenewTokenRenewerParametersSTSRenewSuccessEvent
STSRenewFailureEventblog The different binding implementations support the interface ApplicationEventPublisherAware thus they can publish events about a successful or failed request. You have to provide an implementation of ApplicationListener to listen to Spring Application Events. Due to the usage of generics you can specify which events you want to listen to. All the above STS specific events inherit the abstract class AbstractSTSEvent. If you want to listen to all STS events then you must provide an implementation like this:

public class AllSTSEventsListener implements ApplicationListener {
@Override
public void onApplicationEvent(AbstractSTSEvent event) {
// do whatever you want here
}
}
If you want to listen to all successful issue events you must use the generic STSIssueSuccessEvent. The STS provides the LoggerListener which listens to all STS events and uses the CXF Logging API to write the log message. All you have to do is configure the following bean in the STS application context configuration (ex. cxf-transport.xml):
<bean id="loggerListener" class="org.apache.cxf.sts.event.LoggerListener" />
If you want to configure another Application listeners, just add a bean configuration and you're done.

The LoggerListener is able to log the following context information:

TIME
Creation time of the eventOPERATION
STS binding/operationWS_SEC_PRINCIPAL
Principal in WS-Security tokenSTATUS
Successful/failed requestDURATION
Processing timeTOKENTYPE
Token type requested (SAML 1.1, SAML 2.0, etc)REALM
Security domainAPPLIESTO
Application for which token is requestedCLAIMS
Claims requestedACTAS_PRINCIPAL
Principal of ActAs tokenONBEHALFOF_PRINCIPAL
Principal of On-Behalf-Of tokenVALIDATE_PRINCIPAL
Principal of Validate tokenCANCEL_PRINCIPAL
Principal of Cancel tokenRENEW_PRINCIPAL
Principal of Renew tokenREMOTE_HOST
Hostname/IP which requested the tokenREMOTE_PORT
Source Port which requested the tokenURL
STS URL used to request token The LoggerListener provides the following properties to customize its behaviour:

NameTypeMandatoryDefaultDescriptionlogFieldnameBooleanNoNoShould the fieldname be logged, ex. OPERATION=issuedateFormatStringNogetDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM)Format of the datelogLevelStringNoFINEWhich log level should be used?logStacktraceBooleanNoNoIn case of an error, shall the stacktrace be logged?fieldOrderList<String>NoTIME
STATUS
DURATION
REMOTE_HOST
REMOTE_PORT
OPERATION
URL
REALM
WS_SEC_PRINCIPAL
ONBEHALFOF_PRINCIPAL
ACTAS_PRINCIPAL
VALIDATE_PRINCIPAL
CANCEL_PRINCIPAL
RENEW_PRINCIPAL
TOKENTYPE
APPLIESTO
CLAIMS
EXCEPTIONOrder of context fields to be logged If you want that all LoggerListener related log messages are written into a different file (ex. audit.log) I highly recommend to not use Java Util Logging as it's not so easy to configure a dedicated handler/appender for one logger.

Configure Log4J as the logging framework in CXF (see here)Add the log4j dependency to your POMConfigure the logger and appender

log4j.rootLogger=INFO, CONSOLE, LOGFILE
log4j.logger.org.apache.cxf.sts.event.LoggerListener=DEBUG, AUDIT

# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.Threshold=INFO
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d [%t] %-5p %c %x - %m%n

# AUDIT is set to be a File appender using a PatternLayout.
log4j.appender.AUDIT=org.apache.log4j.FileAppender
log4j.appender.AUDIT.File=${catalina.base}/logs/audit.log
log4j.appender.AUDIT.Append=true
log4j.appender.AUDIT.Threshold=DEBUG
log4j.appender.AUDIT.layout=org.apache.log4j.PatternLayout
log4j.appender.AUDIT.layout.ConversionPattern=%m%n
The audit log file looks like this if configured as above:
5/10/13 8:59:59 AM;SUCCESS;2839ms;127.0.0.1;57378;Issue;https://localhost:9443/fediz-idp-sts/STSService;null;alice;null;null;http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0;https://localhost:8081/doubleit/services/doubleittransportsaml1claims;null;null;
Enjoy. [Less]
Posted 5 days ago
I didn't go to the biennial HotOS conference, which was held last week in New Mexico.

I am, however, extremely grateful to USENIX and to the sponsors and participants for making the technical session materials freely available to all. ... [More]

There's a lot of dig through in the conference, and I'll share my thoughts on some of the work once I've had more of a chance to read it.

Meanwhile, Matt Welsh has stirred up a fair bit of discussion with his post-conference blog post: What I wish systems researchers would work on.

Of the 27 papers presented at the workshop, only about 2 or 3 would qualify as bold, unconventional, or truly novel research directions. The rest were basically extended abstracts of conference submissions that are either already in preparation or will be submitted in the next year or so. This is a perennial problem for HotOS, and when I chaired it in 2011 we had the same problem. So I can't fault the program committee on this one -- they have to work with the submissions they get, and often the "best" and most polished submissions represent the most mature (and hence less speculative) work. Welsh has been involved with the conference for years; moreover, given his well-known move from member of the faculty of Harvard's Computer Science department to lead researcher at Google, he has a fascinating background in both the academic and industrial research arenas.

So his thoughts are worth considering.

And yet, I must say that I find Welsh's criticisms to ring hollow.

It doesn't seem right to critique a topic by whether it is brand new, or whether it "has been a recurring theme" that involves "problems that are 25 years old." In some ways, I think it is a mark of Computer Science's maturity that we've stopped completely changing our entire perspective every 18 months, and are instead returning to deep problems of enduring interest.

For example, the observation by an IBM research team that the last two decades have seen dramatic progress in network I/O performance but a dramatic lack of progress in storage I/O performance seems like a great topic for the operating systems community to be discussing. Why, just last week I was digging into continued research in the network I/O world, while the disk storage world appears to be still stuck in bloated, layered, horrifically complex implementations from gigantic enterprise companies that ladle on the complications while ratcheting up the price: it's not uncommon for an enterprise SAN device to cost 7 or 8 digits, as much as 1000 times the cost of the servers that are trying to process that imprisoned data.

And when Welsh outlines the areas he considers important, he notes that:

A typical Google-scale system involves many interacting jobs running very different software packages each with their own different mechanisms for runtime configuration: whether they be command-line flags, some kind of special-purpose configuration file (often in a totally custom ASCII format of some kind), or a fancy dynamically updated key-value store. and also that: the modes of interaction are vastly more complex and subtle than simply reasoning about state transitions and messages, in the abstract way that distributed systems researchers tend to cast things. I couldn't agree more with Welsh's points, which makes me wonder how he reacted to the session from the RAMCloud team: Toward Common Patterns for Distributed, Concurrent, Fault-Tolerant Code. The RAMCloud developers ran smack into these problems, and felt that pain:

For example, when a server failure notification is received by a server, several of its segments are affected, and each affected segment replica can be in a different state. Some replicas may have an RPC outstanding to the failed server and must abort the RPC and restart replication elsewhere instead of expecting a response. Other affected replicas may not be consistent with their counterparts; such replicas require contacting the cluster coordinator before starting recreation in order to prevent inconsistencies. Now, what the RAMCloud team propose is not so new or trendy; again, they look back two decades, to the dawn of object-oriented design, and the "patterns" approaches that proved so successful in the 1990's:

Although the implementations were different in many respects, we eventually noticed a common theme; each of these modules contained a set of rules that could trigger in any order. We gradually developed a pattern for DCFT code based on three layers: rules, tasks, and pools. This particular pattern has worked for a variety of problems in RAMCloud. We believe that this pattern, or something like it, might provide a convenient way of structuring DCFT modules in general. It's important to look to the future, but it's important to learn from and build on the past. Some old techniques are sound, and we shouldn't jettison them just because they're old (of course, remember you're getting this from a 52-year-old systems programmer who still codes 10 hours a day in C!).

So keep pushing the envelope, but let's not excoriate those who try to help us learn from proven decades-old approaches and bring that wisdom to the problems of modern software. [Less]
Posted 5 days ago
Newegg nukes “corporate troll” Alcatel in third patent appeal win this year

I am loving this. Particularly this:

At trial in East Texas Cheng took the stand to tell Newegg’s story. Alcatel-Lucent’s corporate ... [More] representative, at the heart of its massive licensing campaign, couldn’t even name the technology or the patents it was suing Newegg over. “Successful defendants have their litigation managed by people who care,” said Cheng. “For me, it’s easy. I believe in Newegg, I care about Newegg. Alcatel Lucent, meanwhile, they drag out some random VP—who happens to be a decorated Navy veteran, who happens to be handsome and has a beautiful wife and kids—but the guy didn’t know what patents were being asserted. What a joke.” “Shareholders of public companies that engage in patent trolling should ask themselves if they’re really well-served by their management teams,” Cheng added. “Are they properly monetizing their R&D? Surely there are better ways to make money than to just rely on litigating patents. If I was a shareholder, I would take a hard look as to whether their management was competent.”

(tags: patents ip swpats alcatel bell-labs newegg east-texas litigation lucent)

Call me maybe: Carly Rae Jepsen and the perils of network partitions

Kyle “aphyr” Kingsbury expands on his slides demonstrating the real-world failure scenarios that arise during some kinds of partitions (specifically, the TCP-hang, no clear routing failure, network partition scenario). Great set of blog posts clarifying CAP

(tags: distributed network databases cap nosql redis mongodb postgresql riak crdt aphyr)

The $12 Gongkai Phone

Welcome to the Galapagos of Chinese “open” source. I call it “gongkai” (??). Gongkai is the transliteration of “open” as applied to “open source”. I feel it deserves a term of its own, as the phenomenon has grown beyond the so-called “shanzhai” (??) and is becoming a self-sustaining innovation ecosystem of its own. Just as the Galapagos Islands is a unique biological ecosystem evolved in the absence of continental species, gongkai is a unique innovation ecosystem evolved with little western influence, thanks to political, language, and cultural isolation. Of course, just as the Galapagos was seeded by hardy species that found their way to the islands, gongkai was also seeded by hardy ideas that came from the west. These ideas fell on the fertile minds of the Pearl River delta, took root, and are evolving. Significantly, gongkai isn’t a totally lawless free-for-all. It’s a network of ideas, spread peer-to-peer, with certain rules to enforce sharing and to prevent leeching. It’s very different from Western IP concepts, but I’m trying to have an open mind about it.

(tags: gongkai bunnie-huang china phone mobile hardware devices open-source)

Stability Patterns and Antipatterns [slides]

Michael “Release It!” Nygard’s slides from a recent O’Reilly event, discussing large-scale service reliability design patterns

(tags: michael-nygard design-patterns architecture systems networking reliability soa slides pdf) [Less]
Posted 5 days ago
Fifteen years late, we stumbled across The Legend of 1900.

I suspect that 1900 is the sort of movie that many people despise, and a few people really enjoy.

Count me on the "really enjoy" side. Just knowing that the lead ... [More] character's name was

Danny Boodman T.D. Lemons Novecento was probably enough to hook me. Like any good fantasy, the movie is a parable: about war, about immigration, about society, and perhaps most of all, about the longing that drives people to travel. As 1900 says in a crucial monologue:

I think land people waste a lot of time wondering why. Winter comes and you can't wait for summer, summer comes and you never can wait for winter. That's why you never tire of traveling or chasing some place far away, where it's always summer. I can't tell what sort of reaction you might have to this movie, but if you find yourself on a quiet evening looking for something just a bit unusual to watch, you might give The Legend of 1900 a try. [Less]
Posted 5 days ago
In his talk on performance problems Rainer Schuppe gave a great introduction to
which kinds of performance problems can be observed in production and how to
best root-cause them.

Simply put performance issues usually arise due to a ... [More] difference in either data
volumn, concurrency levels or resource usage between the dev, qa and production
environments. The tooling to uncover and explain them is pretty well known:
Staring with looking at logfiles, ARM tools, using aspects, bytecode
instrumentalisation, sampling, watching JMX statistics, and PMI tools.

All of theses tools have their own unique advantages and disadvantages. With
logs you get the most freedom, however you have to know what to log at
development time. In addition logging is i/o heavy, so doing too much can slow
the application down itself. In a common distributed system logs need to be
aggregated somehow. As a simple example of what can go wrong are cascading
exceptions spilled to disk that cause machines to run out of disk space one
after the other. When relying on logging make sure to keep transaction
contexts, in particular transaction ids across machines and services to
correlate outages. In terms of tool support, look at scribe, splunk and flume.

A tool often used for tracking down performance issues in development is the
well known profiler. Usually it creates lots of very detailed data. However it
is most valuable in development - in production profiling a complete server
stack produces way too much load and data to be feasable. In addition there’s
usually no transaction context available for correlation again.

A third way of watching applications do their work is to watch via JMX. This
capability is built in for any Java application, in particular for servlet
containers. Again there is not transaction context. Unless you take care of it
there won’t be any historic data.

When it comes to diagnosing problems, you are essentially left with fixing
either the “it does not work” case or the “it is slow case”.

For the “it is slow case” there are a few incarnations:

It was always slow, we got used to it.

It gets slow over time.

It gets slower exponentially.

It suddenly gets slow.

There is a spontanous crash.

In the case of “it does not work” you are left with the following observations:

Sudden outages.

Always flaky.

Sporadic error messages.

Silent death.

Increasing error rates.

Misleading error messages.

In the end you will always be spinning in a Look at symptoms, Elimnate
non-causes, Identifiy suspects, Confirm and Eliminate comparing to normal. If
not done with that, leather, rinse, repeat. When it comes to causes for errors
and slowness you will usually will run into one of the following causes: In
many cases bad coding practices are a problem, too much load, missing backends,
resource conflicts, memory and resource leakage as well as hardware/networking
issues are causes.

Some symptoms you may observe include foreseeable lock ups (it’s always slow
after four hours, so we just reboot automatically before that), consistent
slowness, sporadic errors (it always happens after a certain request came in),
getting slow and slower (most likely leaking resources), sudden chaos (e.g.
someone pulling the plug or someone removing a hard disk), and high utilisation
of resources.

Linear memory leak
In case of a linear memory leak, the application usually runs into an OOM
eventually, getting ever slower before that due to GC pressure. Reasons could
be linear structures being filled but never emptied. What you observe are
growing heap utilisation and growing GC times. In order to find such leakage
make sure to turn on verbose GC logging, do heapdumps to find leaks. One
challenge though: It may be hard to find the leakage if the problem is not one
large object, but many, many small ones that lead to a death by 1000 cuts
bleeding the application to death.

In development and testing you will do heap comparisons. Keep in mind that
taking a heap dump causes the JVM to stop. You can use common profilers to look
at the heap dump. There are variants that help with automatic leak detection.

A variant is the pig in a python issue where sudden unusually large objects
cause the application to be overloaded.

Resource leaks and conflicts

Another common problem is leaking resources other than memory - not closing
file handles can be one incarnation. Those problems cause a slowness over time,
they may lead to having the heap grow over time - usually that is not the most
visible problem though. If instance tracking does not help here, your last
resort should be doing code audits.

In case of conflicting resource usage you usually face code that was developed
with overly cautious locking and data integrity constraints. The way to go are
threaddumps to uncover threads in block and wait states.

Bad coding practices

When it comes to bad coding practices what is usually seen is code in endless
loops (easy to see in thread dumps), cpu bound computations where no result
caching is done. Also layeritis with too much (de-)serialisation can be a
problem. In addition there is a general “the ORM will save us all” problem that
may lead to massive SQL statements, or to using the wrong data fetch strategy.
When it comes to caching - if caches are too large, access times of course grow
as well. There could be never ending retry loops, ever blocking networking
calls. Also people tend to catch exceptions but not do anything about them
other than adding a little #fixme annotation to the code.

When it comes to locking you might run into dead-/live-lock problems. There
could be chokepoints (resources that all threads need for each processing
chain). In a thread dump you will typically see lots of wait instead of block
time.

In addition there could be internal and external bottlenecks. In particular
keep those in mind when dealing with databases.

The goal should be to find an optimum for your application between too many too
small requests that waste resources getting dispatched, and one huge request
that everyone else is waiting for. [Less]
Posted 5 days ago
Nearly six years ago, I set up a personal Jabber server using ejabberd.  This setup survived the server migration to Ubuntu 8.04 and 10.04.  This past weekend, I attempted to migrate that to a server running 12.04 and all I could get out of it was ... [More] an erlang crash dump.

A quick scan for successors turned up prosody. Configuration was as simple as adding a VirtualHost and setting allow_registration to true. [Less]
Posted 5 days ago
This is a new Major release of ActiveMQ-CPP which contains bug fixes for issues that were found since v3.6.0 was released along with several new features which have been requested by a number of users.

* Added support of Optimized ... [More] Acknowledge
* Added support for Non-blocking redelivery.
* Removed dependence on the APR-Util library (Still needs the main APR lib).
* Supports priority backups for Failover Transport
* Supports the cluster rebalancing feature.
* Message Audits to filter duplicates on Failover.
* A lot of other little bug fixes and improvements.

Building the project should be a lot simpler now as you only need to have APR v1.3+ installed, this is a big plus for Windows users as building the APR-Util libs on Windows is tricky at best. [Less]
Posted 5 days ago
Today I'll be leaving SourceForge and taking a role at RedHat. Please don't think for a moment that it's because I don't like SourceForge. I continue to think that SourceForge does community *way* better than either Github or Google Code, and while ... [More] there are places where the platform can improve, the team that's working on it is one of the finest bunch of engineers I've ever had the privilege of working with.

Here's a few of the many things I've learned at SourceForge.

People are passionate

Every time I talk to anybody about my job, I mention two projects: PonyKart and OpenMRS. These projects illustrate to me how people can be passionate about anything. Having talked with the leads of both of these projects, I'm blown away by their passion for excellence.

Of course, these projects could hardly be more different.

PonyKart is a My Little Pony themed Mario-Kart style game. It's fun. The physics are well done. The courses are well designed. The community is very engaged. And it has My Little Pony characters in it. The guys that did this project wanted it to be a MLP game, but they also wanted it to be excellent. They wanted it to be fun. They wanted it to be *good*. They are passionate about it.

The OpenMRS project is a medical records system that was developed for a hospital in Kenya that had a hacked-together Access database monstrosity, and it was faster and easier for these guys to hack something together than to try to fix what was there. But that wasn't enough. They were passionate. They wanted it to be done right, and they wanted hospitals all over the world to benefit from it. And now they have a non-profit dedicated to giving this product away to hospitals in developing nations that need it. These guys are my heroes.

I am continually blown away by the quest for excellence, and the vast range of ways that it manifests itself.

People are kind

I've met amazing people in my time at SourceForge. These people are helpful, kind, patient, and, as I've mentioned, passionate. For the most part, people get that I'm human and can't solve all of their problems immediately. They get that we all have the limitation of time and resources.

Most people *don't* throw tantrums or demand their way. For this I am very grateful. I'm glad to have met a few of the nice people.

People are cruel

Sure, SourceForge is the underdog right now. I get that. It's not necessary to be a jerk.

It's hard to remember, when people are being jerks, that they're in the minority. Most people are, in fact, nice. But the jerks are very loud.

I'd like to remind the jerks that the folks who happen to be developing their project on the SourceForge platform are passionate, and they are pragmatic, and they are doing something useful while you fling mud at them.

'nuff said.

People are pragmatic

Tools are tools. They are not your children.

For the most part, people want to get a job done, and they use the tools they have, because the focus is the task, not the tools. Once, we used CVS and MailMan and we *liked* it. SVN is better. Some people like Git better. But if we had to use CVS and MailMan, you know what? We'd still get stuff done.

Religious debates over the relative merits of DVCS and CVCS systems are all well and good over beer at conferences, but most of us have a job to do, and we don't have time for that indulgence. You may, in fact, be right, but I don't have that kind of time.

I grow very weary of the This vs That flame wars that have characterized the IT world for so long. Perl vs Python, VI vs Emacs, Linux vs Windows vs Mac, Git vs SVN. The thing is, if you're a professional, you need to know *all* of them, and you're not coming across as brilliant, you're coming across as only knowing one tool. Nice hammer. Sometimes a screwdriver is useful.

But, much as most people are nice, it turns out most people are pragmatic. Most people don't have time for those debates either. They want to get their job done. I really appreciate having met a lot of those kinds of people. [Less]
 

 
 

Creative Commons License Copyright © 2013 Black Duck Software, Inc. and its contributors, Some Rights Reserved. Unless otherwise marked, this work is licensed under a Creative Commons Attribution 3.0 Unported License . Ohloh ® and the Ohloh logo are trademarks of Black Duck Software, Inc. in the United States and/or other jurisdictions. All other trademarks are the property of their respective holders.