00:00:08.639
hello hello hello thank you for coming
00:00:12.559
my name is Mitrioi I'm a team leader of
00:00:15.599
rub mine team in thread brains and today
00:00:18.960
I will talk about ruby
00:00:20.840
debuggers so we use ruby debuggers quite
00:00:23.680
a lot in our daily working process and
00:00:26.160
we will use them even more because of
00:00:28.080
the code generated by AI I right so with
00:00:31.599
this pressure we need to know our tools
00:00:33.600
even better to stay
00:00:35.399
productive and in this talk I will cover
00:00:38.160
open source Ruby debuggers as well as
00:00:40.239
puby mind debugger I've been working on
00:00:42.879
rub mind debugger for several years and
00:00:45.360
today it's a chance for me to share my
00:00:47.200
experience and show you main concept of
00:00:49.520
ruby
00:00:50.520
debuggers so let's start uh in this talk
00:00:54.879
I will cover five questions first we
00:00:58.000
will take a look on the tools that we
00:00:59.680
have to find bugs in Ruby code after
00:01:03.640
that we will see what technologies power
00:01:06.640
Ruby
00:01:07.960
debuggers then I will show you how do
00:01:10.320
Ruby debuggers work
00:01:12.119
internally and at the next step we will
00:01:14.720
see which debugger is the most
00:01:17.479
performant and at the last part I will
00:01:21.119
take a chance to sh to show you three
00:01:23.280
main uh features of Ruby mind debugger
00:01:26.080
that will help you work more
00:01:27.600
productively
00:01:29.439
so let's start from the first question
00:01:31.680
and let's see what tools do we have to
00:01:34.880
find bugs in Ruby code and let's start
00:01:38.240
with a simple example so here we have a
00:01:40.799
method process that takes one argument
00:01:44.000
inside this method we check whether an
00:01:46.079
argument has two string or inspect
00:01:48.320
methods and if it's true then we print
00:01:50.960
element is printable otherwise we print
00:01:53.439
element is not
00:01:55.000
printable and here in the example we
00:01:57.600
have two calls at the bottom uh the
00:01:59.840
first call process of five prints
00:02:01.840
element is printable as it should but
00:02:04.880
the second call looks pretty suspicious
00:02:08.399
it prints element is printable but basic
00:02:11.680
object doesn't have to string or inspect
00:02:13.599
methods at
00:02:14.760
all and yep apparently there is a bug
00:02:18.560
here in this code
00:02:20.640
so let's think for a second and please
00:02:23.440
raise your hand if you know where the
00:02:25.120
problem
00:02:26.520
is don't worry I will not ask you about
00:02:28.879
that i will write answer on the next
00:02:31.239
slide if you know where the problem is
00:02:33.760
raise your
00:02:35.080
hand yep yep great so the real problem
00:02:39.840
is here this part goes as an argument to
00:02:43.840
the defined method and in this way our
00:02:46.879
method process print element is
00:02:48.879
printable regardless of the argument
00:02:51.120
even with
00:02:52.599
Neil yeah this is a problem we can fix
00:02:55.200
it just by adding a couple of
00:02:57.360
parentheses for the first for the first
00:02:59.680
part of the if condition and after that
00:03:02.800
our output will be correct
00:03:06.400
this is a pretty simple example right
00:03:09.040
with almost obvious
00:03:11.000
bug but what tools could we have could
00:03:15.200
be used to find real bugs in real Ruby
00:03:18.440
code and the first thing that comes in
00:03:21.280
mind is put statement right so we can
00:03:24.400
place several put statements here and
00:03:26.400
there in our code and they will give us
00:03:28.400
some hints about where the problem is or
00:03:30.799
what we need to do next to investigate
00:03:32.560
our problem
00:03:33.959
further at the same time we could use
00:03:36.560
puts debugger gem that makes our output
00:03:39.440
more distinguishable between each other
00:03:42.640
and also we could use awesome gem
00:03:44.720
awesome print gem that makes our output
00:03:48.080
more structured and easy to
00:03:50.840
read generally put statements are really
00:03:54.000
helpful but they're too basic and for
00:03:56.720
something more advanced uh we need a
00:03:58.720
more complicated uh tool and we have
00:04:01.920
this tool we have interactive
00:04:04.280
consoles uh in Ruby we have mainly Ruby
00:04:07.760
and Pry consoles as you know uh we still
00:04:10.640
need to modify our source code to make
00:04:12.480
them work uh we need to place a call to
00:04:14.799
a certain place in our code and here
00:04:18.239
this tool is more advanced test than
00:04:21.359
puts debugger and put statements but
00:04:24.479
they still lack of one important feature
00:04:26.560
the lack of execution control and the
00:04:28.639
lack of
00:04:29.800
stepping and the next
00:04:32.040
tool Ruby debugger has all necessary
00:04:35.680
features uh like stepping break points
00:04:39.199
context and frame introspection
00:04:41.120
everything we need and this tool is the
00:04:43.360
most advanced tool in Ruby ecosystem for
00:04:45.600
investigating bugs in Ruby
00:04:47.880
code but before we start with debuggers
00:04:50.720
let's ask
00:04:52.120
ourself how often do Ruby developers use
00:04:55.280
a
00:04:57.080
debugger and the answer is here on the
00:04:59.919
slide so every third run is a debug run
00:05:03.840
according our stats in Ruby mine it
00:05:07.120
means that pub debuggers at the crucial
00:05:09.919
part of our development processes and
00:05:12.560
that's why I'm here that's why I'm
00:05:14.080
talking about Ruby debuggers if we
00:05:16.560
become more productive with debuggers
00:05:18.400
then we become more productive in our
00:05:20.160
work every day
00:05:23.240
so let's move to the next step and let's
00:05:26.320
see what technologies power Ruby
00:05:29.479
debuggers the first technology is trace
00:05:32.840
point it was introduced long time ago in
00:05:35.759
2y 2.0 and it mainly provides an ability
00:05:38.639
to execute some specific code on a
00:05:40.800
certain event in your code at the
00:05:43.479
runtime it works almost everywhere it
00:05:46.479
works in threads it works in fibers but
00:05:49.199
it doesn't work in recctor yet
00:05:52.600
unfortunately and let me show you an
00:05:54.639
example how does it work so here we have
00:05:57.600
a method say hello and we have a trace
00:06:00.720
point targeted to call events at the
00:06:04.000
bottom we have a call of our method and
00:06:06.240
we have our output in comments so as you
00:06:09.039
can see first we have a message from the
00:06:10.800
trace point and only after that we have
00:06:13.120
a message from the method
00:06:16.039
itself having only trace point we can
00:06:19.199
even create probably the simplest
00:06:21.680
possible debugger in Ruby and this part
00:06:25.440
is the main difference from the previous
00:06:28.199
example so in this part we take an input
00:06:30.960
from the user and we try to evaluate it
00:06:33.520
and we print the results to the
00:06:36.440
console in this way we will be able to
00:06:38.880
introspect the context on each call
00:06:41.680
event it means that we will be able to
00:06:44.960
uh
00:06:45.720
debug like essentially every method
00:06:49.960
invocation and that's looks so simple
00:06:52.960
right so it's impossible to make it uh
00:06:55.280
that simple without trace point and
00:06:57.600
that's why trace point is a crucial
00:06:59.280
scene for any Ruby
00:07:01.720
debugger the next technology is proy
00:07:05.120
virtual machine instruction
00:07:07.720
sequence instruction sequence in short A
00:07:11.360
sec is essentially a representation of
00:07:14.080
compiled by code for Ruby virtual
00:07:16.199
machine it's tightly coupled to Ruby
00:07:18.639
version uh virtual machine and that's
00:07:21.680
why it depends on the Ruby
00:07:23.720
version uh and this technology mainly
00:07:28.560
provides an access to low-level
00:07:31.039
representation of your Ruby code so with
00:07:34.240
this technology you can modify your bite
00:07:36.479
code without touching the source code
00:07:38.720
and that way you can adjust behavior you
00:07:40.720
can increase performance of your
00:07:42.160
application and other opportunities
00:07:46.120
here and let me show you an example how
00:07:49.039
we can get instruction sequence of the
00:07:51.120
method in Ruby
00:07:52.840
code so here we have the same method say
00:07:55.840
hello with put statement and then we
00:07:58.560
take a method object from this method
00:08:01.440
and we pass that method object to
00:08:03.039
instruction sequence method at the end
00:08:05.919
we just call this method just to get
00:08:08.240
human readable representation of
00:08:10.000
instruction sequence and let's take a
00:08:13.440
look at the result
00:08:15.479
right
00:08:17.560
so here what we have we have several
00:08:20.400
lines several instructions and they're
00:08:23.039
pretty readable we can even read uh our
00:08:26.479
argument for our puts statement it's
00:08:29.120
hello Rubik 2025 that's great and there
00:08:33.279
is another one thing here uh these marks
00:08:37.120
uh these marks represent events that
00:08:38.959
will be emitted at the runtime at the
00:08:41.039
certain instruction and this is a way
00:08:43.760
how instruction sequence collaborates
00:08:45.680
with trace point trace point targets to
00:08:48.800
these uh events at the runtime and
00:08:51.440
that's how they work
00:08:54.200
together so that's it about technologies
00:08:57.600
we have trace point and instruction
00:09:01.000
sequence and next let's take a look at
00:09:03.839
how Ruby debuggers
00:09:05.959
work and I would like to start with
00:09:08.320
pybug debugger it's well-known debugger
00:09:11.279
it has all necessary features break
00:09:13.440
points stepping context introspection
00:09:15.839
everything that we need it default
00:09:18.160
debugger for old Ruby and old trails
00:09:20.240
versions u it still requires
00:09:22.800
modification to our source code to make
00:09:25.760
it work usually we need to also place a
00:09:28.720
call somewhere or configure our project
00:09:30.800
to make it work and by default there is
00:09:33.839
a command line interface for this tool
00:09:36.480
but you can fight a bit and configure VS
00:09:38.959
code plug-in to work with graphical user
00:09:42.839
interface this is the main highlights
00:09:45.680
but how does work
00:09:49.080
right and this is a simple model of bbug
00:09:53.120
debugger so all this code should be
00:09:55.279
invoked before the code that we are
00:09:58.160
going to
00:09:59.000
debug and at the top place we have two
00:10:02.240
lists list of break points and list of
00:10:04.320
trace points in the list of trace points
00:10:06.959
we have a trace point per per each event
00:10:09.680
type so one trace point for call event
00:10:12.160
one trace point for return event and so
00:10:15.079
on and each trace point our sensor is
00:10:18.640
the same they have the same body the
00:10:20.720
same code in each trace point we try to
00:10:23.519
find a break point at the certain place
00:10:25.920
where we are and if we find it then we
00:10:28.959
pass our control to the user otherwise
00:10:31.519
we just continue our execution without
00:10:33.760
any additional steps till the next
00:10:37.640
event this approach works it works
00:10:40.320
reliably it allow us to debug our code
00:10:43.279
but there is one issue so we do a lot of
00:10:46.880
unnecessary checks on the each event to
00:10:49.440
find our break points at the runtime and
00:10:52.320
that's the main problem because it leads
00:10:54.399
to performance
00:10:56.760
degradation and the next tool debug gem
00:11:00.880
overcome this performance issue but
00:11:02.880
before we start with debug gem uh let's
00:11:05.040
take a look at the main feature that
00:11:06.800
allows this debugger to overcome this
00:11:09.200
performance
00:11:10.440
issue and this feature is trace point
00:11:13.720
improvements respoint improvements were
00:11:16.079
introduced in Ruby 2.6 and it mainly
00:11:18.959
provides an ability to target our trace
00:11:21.839
point to a certain line or
00:11:24.680
even in that way we don't need to check
00:11:27.680
every event for a break point present uh
00:11:30.560
we can create a trace point and we can
00:11:33.519
target our trace point to a certain
00:11:35.519
place where we have a break point for
00:11:38.600
sure in this way we don't need to do any
00:11:41.519
unnecessary work and it's why it works
00:11:44.560
fast without any performance
00:11:47.480
overhead let me show you an example how
00:11:49.760
does it work just to illustrate this
00:11:51.760
feature and here we have two methods say
00:11:54.800
hello and say goodbye we have the same
00:11:56.880
trace point as before targeted to call
00:11:59.480
events and the difference is here so we
00:12:02.959
target our trace point
00:12:04.760
to the is of the first method and at the
00:12:08.639
bottom we can see that our trace point
00:12:10.399
was triggered only once for this first
00:12:12.480
method and was ignored for the second
00:12:14.519
one that's the main
00:12:18.040
feature well debug gem uses this
00:12:21.200
approach to overcome performance issue
00:12:23.839
and in fact this debugger doesn't have
00:12:25.920
any performance degradation and that's a
00:12:28.720
great achievement over by bug debugger
00:12:32.959
also this tool supports Ruby starting
00:12:35.360
from 2.7 and greater and it's mainly
00:12:38.320
because the fact that trace point
00:12:40.480
improvement wasn't ported to the
00:12:42.399
previous versions um so only modern Ruby
00:12:45.959
supported apart of that uh there is
00:12:50.160
there are several uh front ends for this
00:12:52.160
debugger you can use uh VS code you can
00:12:55.279
use Chrome so a couple options here and
00:12:59.360
the main scene that this debugger is
00:13:01.279
bundled in Ruby starting from
00:13:03.959
3.1 and that means that this is a
00:13:06.399
default option for debugging your code
00:13:09.120
uh in the modern Ruby especially if you
00:13:11.519
don't have any other tool like Ruby mind
00:13:14.920
debugger and that's it about open source
00:13:17.920
uh Ruby debuggers and let's take a look
00:13:20.720
what Ruby mind debugger could
00:13:23.639
offer first uh This debugger is bundled
00:13:26.959
in Ruby mine uh by default with
00:13:29.040
graphical user interface tailored for
00:13:31.040
developers productivity this debugger
00:13:33.760
provides seamless debugging experience
00:13:35.760
uh for example there is a feature that
00:13:37.680
you can launch this debugger without any
00:13:40.240
prior configuration just in one click
00:13:42.959
the ID that's great apart of that uh
00:13:47.040
this tool supports Ruby starting from
00:13:49.120
2.3 and greater so almost all possible
00:13:52.480
Ruby that your application could use are
00:13:55.240
supported and with all these supported
00:13:57.760
Ruby versions uh this debugger works
00:14:00.160
fast so it works fast even with old
00:14:03.720
Ruby and last touch here is that there
00:14:07.519
is an ability to attach this tool to any
00:14:09.680
Ruby process so you don't need to start
00:14:12.079
and launch a debugger right from the
00:14:13.760
start you can attach this tool to
00:14:16.320
already existing process when you really
00:14:18.639
need it to debug
00:14:21.920
that's the main highlights and yeah what
00:14:25.199
is like how is mind debugger
00:14:28.760
structured and this is a general
00:14:31.360
architecture of ruby mind debugger it
00:14:33.920
contains three
00:14:35.720
components so the first part is debugger
00:14:38.959
back end essentially it's the base gem
00:14:42.959
it's responsible for low-level scenes
00:14:45.360
like uh context and frame retrieving
00:14:48.560
manipulating with ISX and trace points
00:14:51.120
and that's mainly a C
00:14:53.880
extension then we have another component
00:14:56.720
debugger front end uh Ruby debug ID gem
00:15:01.760
this component is responsible for
00:15:03.440
connection between Ruby mine id and
00:15:05.440
debugger itself and for text
00:15:08.480
representation of Ruby values that will
00:15:10.639
be presented later in the Ruby mine uh
00:15:15.160
ID and the last part uh program ID
00:15:18.959
itself
00:15:20.440
uh this component is mainly about user
00:15:24.079
interface and developers productivity so
00:15:26.560
almost all fancy features targeted to
00:15:28.880
developers productivity located there
00:15:31.600
and that's how it works in
00:15:34.120
general so this is a general
00:15:36.399
architecture uh but the real
00:15:39.279
architecture looks a bit more
00:15:41.279
complicated
00:15:43.040
so instead of one branch of gems we have
00:15:45.920
two branches two
00:15:48.720
uh the first branch is targeted to call
00:15:51.360
uh to old Ruby and it uses old Ruby API
00:15:55.759
it contains several low-level hacks that
00:15:58.639
uh helps us achieve desired performance
00:16:01.519
and it's really hard to maintain and add
00:16:03.440
new features because of these
00:16:05.399
hacks so we decided to create another
00:16:08.720
branch for modern Ruby it uses modern
00:16:12.000
Ruby API it doesn't have any major hacks
00:16:16.160
behind and uh in that way we are able to
00:16:20.399
maintain and manage our debugger
00:16:25.560
easily so this it about debuggers
00:16:29.399
perform internals and let's figure out
00:16:33.440
which debugger is the most performant
00:16:37.120
to find this out I created a simple
00:16:41.079
experiment we have Fibonacci method with
00:16:44.160
a meaningless break point that we never
00:16:46.759
hit and we need a minimal breakpoint
00:16:49.600
just to measure how a break point could
00:16:51.920
affect our debugger
00:16:54.440
performance also in this experiment I
00:16:57.759
used 100 runs just to get our results
00:17:00.480
stable and reliable
00:17:02.959
and apart of that I used all latest
00:17:06.400
versions of Ruby debuggers available on
00:17:08.880
the date so we have two groups for old
00:17:12.160
Ruby and for modern Ruby and as you can
00:17:14.799
see remain debugger presents in both of
00:17:17.199
them but with different sets of uh gem
00:17:21.959
versions and yeah let's take a look at
00:17:24.319
the
00:17:27.079
results here they are first I would like
00:17:30.640
to highlight that by bug debugger works
00:17:33.120
slow it more than 20 25 times slower
00:17:36.960
than original run without any
00:17:40.440
debugger and at the same old Ruby Ruby
00:17:43.520
mind debugger works fast without any
00:17:45.520
visible performance issue and that means
00:17:48.720
that for the old Ruby we have only one
00:17:51.440
option to debug our code without
00:17:54.200
debugger with decradation
00:17:57.440
that's not really great because we have
00:17:59.039
only one option for that but we have a
00:18:02.080
better case for the modern Ruby in
00:18:04.720
modern Ruby we have debug gem and ruby
00:18:06.960
mind debugger they are both uh work fast
00:18:09.840
uh without any performance issue and in
00:18:12.559
modern Ruby we can choose uh what we
00:18:15.200
need to use for each particular
00:18:18.200
case so now we know that debug gem and
00:18:21.760
man debugger works fast and let's move
00:18:24.720
to the last part and let me show you
00:18:27.840
three unique features of Ruby mind
00:18:29.600
debugger that will help you work more
00:18:32.679
productively the first feature is debug
00:18:35.760
rails application in one
00:18:38.280
click suppose we have a rails
00:18:40.640
application it's really regular rails
00:18:43.200
application based on Puma server and
00:18:45.679
here on the screen we have a config file
00:18:49.120
for our Puma server um as you can see we
00:18:52.799
have uh five workers and
00:18:56.200
uh two threads right uh so in that way
00:19:00.840
we deal with multi- thread multiprocess
00:19:04.000
rails application and suppose we need to
00:19:06.799
debug this application uh suppose we we
00:19:09.440
need to debug our method home in our
00:19:11.440
controller pretty usual scene and first
00:19:15.840
step we need to do is to add a break
00:19:18.000
point by one click on the gutter here it
00:19:21.880
is and on after that all we need to
00:19:25.120
start our debugger is just one click on
00:19:27.600
the debug button in the IDE and that's
00:19:30.919
it after that debugger will start and we
00:19:34.160
will be able to hit our break point uh
00:19:36.720
and see this view so here we have list
00:19:40.000
of frames panel with variables and
00:19:42.240
values controls uh for navigation during
00:19:45.280
the debugging process execution console
00:19:47.919
and other
00:19:49.080
things and the feature is that we are
00:19:51.520
able to launch our debugger without any
00:19:54.480
prior configuration without dealing with
00:19:57.039
commands in terminal without modifying
00:19:59.600
our source code just in one click it
00:20:03.120
works out of the out of the box without
00:20:05.360
any prior configuration and it saves
00:20:07.440
your time on
00:20:10.200
that another feature is smart
00:20:13.960
stepping so suppose we have this example
00:20:17.360
this code we have a class my printer
00:20:19.760
with several printing methods and we
00:20:22.400
have a chain of calls at the
00:20:24.440
bottom so let's say we need to debug
00:20:26.880
this chain of calls uh so we put a break
00:20:29.600
point on this line uh we started our
00:20:32.000
debugger we stopped on this line and
00:20:34.159
here we are we need to debug uh this
00:20:36.799
line
00:20:38.400
so how we do it in Ruby
00:20:40.360
Mine ruby Mine debugger provides several
00:20:43.360
convenient actions for navigation during
00:20:45.679
the debugging process they called step
00:20:47.840
into step out and step
00:20:50.200
over and let's hear them so first let's
00:20:54.159
call step and remind provides us several
00:20:57.360
options where we can actually step into
00:21:00.880
and let's choose that second option and
00:21:02.960
let and let's step into our first method
00:21:05.440
in the class here we are uh then suppose
00:21:09.919
we investigated something here and we
00:21:13.039
would like to get back to our initial
00:21:15.679
line where we were at the beginning and
00:21:19.039
for that purpose uh yeah for that
00:21:21.280
purpose we can call uh step
00:21:24.039
out and here we are on the same on the
00:21:27.919
exactly the same line that's impossible
00:21:30.240
to achieve uh the same behavior with
00:21:32.400
chain of calls with other
00:21:34.360
debuggers after that we can uh repeat
00:21:37.440
our actions we can call step into
00:21:40.840
and now we have two options here let's
00:21:44.720
choose the first one and let's step into
00:21:46.720
our second method in the class
00:21:49.600
um suppose uh at that point we need to
00:21:53.039
uh investigate something in this method
00:21:55.679
uh in more details and we need to step
00:21:58.480
inside this method without going outside
00:22:00.799
and without going deeper in the calls uh
00:22:04.400
so for that purpose we can call step
00:22:06.400
over and in this way we can step into
00:22:08.880
inside the
00:22:10.760
method so last step is just to call step
00:22:14.320
out and get back to our initial line
00:22:17.360
where we were at the
00:22:19.000
beginning and that's how it works it
00:22:21.520
works reliably in simple cases it works
00:22:24.240
in complicated cases like chain of calls
00:22:27.200
and it saves your time on the navigation
00:22:29.039
during the debugging
00:22:32.039
process so and the last uh feature that
00:22:35.360
I would like to show you
00:22:36.919
today is inline debugger values
00:22:41.520
so suppose we need we have this example
00:22:44.240
this code we have a method uh filter
00:22:48.080
array that filters array in some way
00:22:50.720
doesn't matter right now and suppose we
00:22:53.520
need to debug this method we have a
00:22:55.280
break point on the line four and uh we
00:22:58.960
launch our debugger we stopped on this
00:23:01.039
line and here we
00:23:03.240
are in this view we have panel with
00:23:06.159
variables and values and usually during
00:23:09.600
the debugging process
00:23:11.200
We need to match variables in the editor
00:23:14.159
with values in this panel so we need to
00:23:17.120
go back and forth to make this kind of
00:23:19.280
map in our mind and it takes a lot of
00:23:22.240
resources to keep it
00:23:24.440
consistent and yeah that's not really
00:23:26.720
efficient way ruby mind provides a
00:23:29.679
better solution it provides these inline
00:23:32.559
hints right in the editor they show you
00:23:35.840
variables and values uh near the place
00:23:38.240
where variables are right in the editor
00:23:41.280
with this feature you don't need to go
00:23:42.720
back and forth you can see it right on
00:23:44.799
the place and it saves your mind
00:23:49.240
resources that's it about features that
00:23:52.320
I have today and let's
00:23:55.159
recap so first we discussed that uh
00:23:58.480
puberty debuggers mainly use trace point
00:24:01.039
and instruction sequence there are two
00:24:03.120
core technologies for any Ruby
00:24:06.120
debugger then we discussed that debug
00:24:08.720
gem works fast due to thoint
00:24:10.799
improvements and that's a great
00:24:12.400
achievement over play by bback
00:24:14.919
debugger and the last scene about ruby
00:24:17.679
mind debugger it works fast with all
00:24:20.400
supported Ruby versions it provides it
00:24:24.159
provides oneclick debugging experience
00:24:26.320
for rails application that saves your
00:24:28.240
time on the configuration
00:24:31.679
it streamlines the bugging experience
00:24:33.200
with smart stepping and helps you save
00:24:35.440
your time on the navigation during the
00:24:37.360
debugging process and the last thing
00:24:39.919
that uh it provides inline debugger
00:24:42.159
values that helps you debug with
00:24:44.080
pleasure and save your mind
00:24:47.000
resources so that's it uh please take
00:24:50.640
your phones and scan your square code to
00:24:52.799
try and remind debugger for free share
00:24:56.480
your feedback with me on LinkedIn and
00:24:58.640
also we have a boost on this conference
00:25:00.720
so contrast to talk about Ruby Rails and
00:25:04.799
Ruby min that's it thank you