ruby

Czego programiści .NET mogliby nauczyć się od Rubystów?

.NET był wybraną przeze mnie technologią niemal od początku mojej przygody z programowaniem. Uważam jednak, że programista nie powinien się zamykać w jednej szyfladce i poznawać szerokie spectrum technologii. Porównywać je i znać wady oraz zalety każdej z nich. Ostatecznie zaś umieć dobrać właściwą technologię do rozwiązania napotykanych problemów – nie odwrotnie ;)

Stąd też pytanie w temacie. Czy faktycznie programiści .NET mogą nauczyć się czegoś ze środowiska Ruby, języka, który ostatnimi czasy zbiera niemało batów (głównie związanych z wydajnością oraz bezpieczeństwem jego najpopularniejszej platformy, RoRa)? Otóż jak się okazuje całkiem sporo, nawet pomimo już istniejącego trendu absorbowania do platformy .NET sporej ilości rozwiązań napotykanych w konkurencyjnych gałęziach. Poniżej postaram się wymienić te, które moim zdaniem są warte odnotowania.

1. Przestań polegać na swoim IDE

Nie da się tego ukryć – Visual Studio jest jednym z najlepszych, o ile nie najlepszym IDE, z jakim programista może mieć obecnie przyjemność pracować. Ma to jednak pewną cenę. W pewnym momencie zaczynamy polegać na swoim IDE w stopniu większym, niż na własnych umiejętnościach. Co gorsza przenosi się to bezpośrednio na tworzone biblioteki (przykład… Entity Framework – czy ktoś próbował kiedyś stworzyć model edmx bez pomocy wizarda wbudowanego w Visual Studio?). Jeżeli nie możesz sprawnie korzystać z biblioteki bez wsparcia swojego IDE, oznacza to, że jest ona zbyt skomplikowana i najzwyczajniej w świecie zepsuta.

W przypadku Ruby’ego, narzędzie pracy jest kwestią gustu. Jednakże kiedy pierwszy raz przyszło mi pracować z teamem RoR każde z nich w porównaniu do Visual Studio wygląda jak notatnik z kolorowaniem składni. Żadnego zaawansowanego refactoringu, intellisense, nawigacji między plikami w projekcie, generowania buildów. Co było w tym najzabawniejsze? Po 2 tygodniach wcale mi tego nie brakowało. Przysłowiowy notatnik + konsola całkowicie wystarczały do pracy, zaś narzędzia są skonstruowane tak, aby programista był sobie w stanie poradzić ze wszystkim zaraz po zainstalowaniu interpretera.

2. Debuger jest opcją, nie obowiązkiem

W świecie .NET jest to dziwna opinia. Wielu programistów wykorzystuje go jako normalne narzędzie pracy służące sprawdzeniu, czy aplikacja działa poprawnie. Dodatkowo sam Visual Studio ułatwia takie podejście. Jako programista Ruby nigdy nie musiałem skorzystać z debuggera – prawdę mówiąc, nigdy go nie zainstalowałem. Nie oznacza to oczywiście, że kod napisany w Rubym jest zawsze właściwy. Wynika to z innego podejścia. W tym środowisku standardem jest pisanie unit testów do weryfikacji każdej bardziej złożonej części systemu. Dzięki temu testy w Rubym są domyślnym must have przy każdym projekcie.

3. Nie bój się eksperymentować

To co najbardziej spodobało mi się we współpracy z programistami Ruby, to otwartość na nowinki, chęć wypróbowania innych podejść. Wiąże się z tym pewne ryzyko, ale również określony zysk. Tutaj powstaje pewien problem – ryzyko w skostniałym świecie aplikacji biznesowych z reguły jest czymś nie do przyjęcia. Dodatkowym hamulcem jest tutaj fakt, że wielu starszych programistów ze środowiska .NET nieprzychylnie patrzy na rozwiązania open source (w końcu kogo wtedy pociągnąć do odpowiedzialności?) oraz ogólnie technologiom spoza kółka Microsoftu.

Nie należy bać się zmian. Nawet jeżeli ryzyko jest nieakceptowalne w aplikacjach dla klientów zewnętrznych, wciąż możemy wprowadzać drobne zmiany dla systemów używanych wewnątrz firmy oraz wypróbowywać tam nowe podejścia. Z jednej strony może to przełożyć się na zysk w przyszłości, z drugiej działa to jak magnes na programistów-pasjonatów, a takich nigdy nie ma za wielu, prawda? ;)

Zapowiedź Ruby 2.1 - generational heap

Nie minęło wiele czasu od wydania Ruby’ego w wersji 2.0, a już pojawiła się ciekawa nowinka – od wersji 2.1 MRI ma wspierać generacje na zarządzanej stercie. Była to dla mnie wiadomość o tyle ciekawsza, że do tej pory sądziłem, że nie jest to możliwe bez zerwania kompatybilności z niskopoziomowymi rozszerzeniami języka C, które znajdują się w niektórych bibliotekach – tymczasem zapewniono, że żadna tego typu sytuacja nie będzie mieć miejsca.

Generacje w Rubym (MRI)

Jak więc ma wyglądać nowy mechanizm GC? Do tej pory wiadomo niewiele, dlatego postaram się podzielić szczegółami, które udało mi się zebrać. Nowy garbage collector nosi nazwę RGenGC i domyślnie będzie dzielił stertę na dwie generacje – często odśmiecana przestrzeń niewielkiego rozmiaru dla obiektów krótkotrwałych (obiekty tworzone w zakresach lokalnych metod często kończą tu swój żywot) oraz przestrzeń obiektów długotrwałych, awansowanych z pierwszej generacji.

Dotychczasowym problemem była konieczność zmiany adresów obiektów, związana z przenoszeniem obiektu pomiędzy generacjami. O ile MRI mogłoby śledzić takie “ruchome” instancje, o tyle rozszerzenia napisane w języku C nie są w stanie tego zrobić (wewnętrznie referencje na obiekty w Rubym są po prostu zwykłymi wskaźnikami). Jak rozwiązano ten problem? Postanowiono podzielić referencje na obiekty na dwie klasy:

  • Lśniące (shiny) – obiekty z takimi referencjami mogą być swobodnie śledzone i przenoszone pomiędzy generacjami zarządzanej sterty.
  • Ciemne (shady) – w momencie, kiedy jakiś obiekt zostanie wykorzystany z poziomu C, zostaje on przypisany do tej klasy. Obiekty tego typu nie mogą być przenoszone ani zebrane przez kolektor, dopóki są wykorzystywane przez kod niezarządzany.

Przypomina to znane z innych środowisk mechanizmy współpracy pomiędzy kodem wysoko- i niskopoziomowym (w .NET takie “nieprzenaszalne” obiekty noszą nazwę pinned objects).

Jaki ma to wpływ na wydajność? Dotychczas nie stworzono oficjalnych benchmarków, nieoficjalnie mówi się o kilku procentowej poprawie względem poprzedniego rozwiązania. Należy jednak pamiętać, że prace wciąż trwają i wiele może się jeszcze zmienić.